From 90d2dce07aab45e7f2b023e868878ccac7fe054d Mon Sep 17 00:00:00 2001 From: Drake Arconis Date: Sun, 10 Aug 2014 17:41:10 -0400 Subject: [PATCH 01/55] Add FMOD Studio support --- indra/cmake/CMakeLists.txt | 2 +- indra/cmake/CopyWinLibs.cmake | 42 +- indra/cmake/FMOD.cmake | 52 - indra/cmake/FMODEX.cmake | 4 + indra/cmake/FMODSTUDIO.cmake | 83 ++ indra/llaudio/CMakeLists.txt | 48 +- indra/llaudio/llaudioengine_fmod.cpp | 789 ------------- indra/llaudio/llaudioengine_fmod.h | 124 -- indra/llaudio/llaudioengine_fmodstudio.cpp | 1016 +++++++++++++++++ indra/llaudio/llaudioengine_fmodstudio.h | 139 +++ indra/llaudio/lllistener_fmod.cpp | 125 -- indra/llaudio/lllistener_fmod.h | 58 - indra/llaudio/lllistener_fmodstudio.cpp | 141 +++ indra/llaudio/lllistener_fmodstudio.h | 71 ++ indra/llaudio/llstreamingaudio_fmod.cpp | 385 ------- indra/llaudio/llstreamingaudio_fmod.h | 68 -- indra/llaudio/llstreamingaudio_fmodstudio.cpp | 505 ++++++++ indra/llaudio/llstreamingaudio_fmodstudio.h | 87 ++ indra/llmath/llmath.h | 2 +- indra/newview/CMakeLists.txt | 95 +- indra/newview/llstartup.cpp | 31 +- 21 files changed, 2157 insertions(+), 1710 deletions(-) delete mode 100644 indra/cmake/FMOD.cmake create mode 100644 indra/cmake/FMODSTUDIO.cmake delete mode 100644 indra/llaudio/llaudioengine_fmod.cpp delete mode 100644 indra/llaudio/llaudioengine_fmod.h create mode 100644 indra/llaudio/llaudioengine_fmodstudio.cpp create mode 100644 indra/llaudio/llaudioengine_fmodstudio.h delete mode 100644 indra/llaudio/lllistener_fmod.cpp delete mode 100644 indra/llaudio/lllistener_fmod.h create mode 100644 indra/llaudio/lllistener_fmodstudio.cpp create mode 100644 indra/llaudio/lllistener_fmodstudio.h delete mode 100644 indra/llaudio/llstreamingaudio_fmod.cpp delete mode 100644 indra/llaudio/llstreamingaudio_fmod.h create mode 100644 indra/llaudio/llstreamingaudio_fmodstudio.cpp create mode 100644 indra/llaudio/llstreamingaudio_fmodstudio.h diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index d2e9f2249..c6d716fed 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -29,8 +29,8 @@ set(cmake_SOURCE_FILES ELFIO.cmake EXPAT.cmake ExamplePlugin.cmake - FMOD.cmake FMODEX.cmake + FMODSTUDIO.cmake FindAPR.cmake FindBerkeleyDB.cmake FindCARes.cmake diff --git a/indra/cmake/CopyWinLibs.cmake b/indra/cmake/CopyWinLibs.cmake index 17d5eab02..fb5093f1b 100644 --- a/indra/cmake/CopyWinLibs.cmake +++ b/indra/cmake/CopyWinLibs.cmake @@ -233,6 +233,29 @@ if(WORD_SIZE EQUAL 32) ) endif(WORD_SIZE EQUAL 32) +if(FMODSTUDIO) + if (WORD_SIZE EQUAL 32) + set(fmodstudio_dll_file "fmod.dll") + else (WORD_SIZE EQUAL 32) + set(fmodstudio_dll_file "fmod64.dll") + endif (WORD_SIZE EQUAL 32) + + find_path(FMODSTUDIO_BINARY_DIR "${fmodstudio_dll_file}" + "${release_src_dir}" + "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib" + "${FMODSTUDIO_SDK_DIR}" + NO_DEFAULT_PATH + ) + + if(FMODSTUDIO_BINARY_DIR) + copy_if_different("${FMODSTUDIO_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Release" out_targets "${fmodstudio_dll_file}") + set(all_targets ${all_targets} ${out_targets}) + copy_if_different("${FMODSTUDIO_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" out_targets "${fmodstudio_dll_file}") + set(all_targets ${all_targets} ${out_targets}) + copy_if_different("${FMODSTUDIO_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Debug" out_targets "${fmodstudio_dll_file}") + set(all_targets ${all_targets} ${out_targets}) + endif(FMODSTUDIO_BINARY_DIR) +endif(FMODSTUDIO) if(FMODEX) if (WORD_SIZE EQUAL 32) @@ -258,25 +281,6 @@ if(FMODEX) endif(FMODEX_BINARY_DIR) endif(FMODEX) -if(FMOD) - find_path(FMOD_BINARY_DIR fmod.dll - ${release_src_dir} - ${FMOD_SDK_DIR}/api - ${FMOD_SDK_DIR} - ) - -if(FMOD_BINARY_DIR) - copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Release" out_targets fmod.dll) - set(all_targets ${all_targets} ${out_targets}) - copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" out_targets fmod.dll) - set(all_targets ${all_targets} ${out_targets}) - copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Debug" out_targets fmod.dll) - set(all_targets ${all_targets} ${out_targets}) - else(FMOD_BINARY_DIR) - list(APPEND release_files fmod.dll) #Required for compile. This will cause an error in copying binaries. - endif(FMOD_BINARY_DIR) -endif(FMOD) - copy_if_different( ${release_src_dir} "${CMAKE_CURRENT_BINARY_DIR}/Release" diff --git a/indra/cmake/FMOD.cmake b/indra/cmake/FMOD.cmake deleted file mode 100644 index a2d9245ff..000000000 --- a/indra/cmake/FMOD.cmake +++ /dev/null @@ -1,52 +0,0 @@ -# -*- cmake -*- - -include(Linking) - -if(INSTALL_PROPRIETARY) - include(Prebuilt) - use_prebuilt_binary(fmod) -endif(INSTALL_PROPRIETARY) - -find_library(FMOD_LIBRARY - NAMES fmod fmodvc fmod-3.75 - PATHS - optimized ${ARCH_PREBUILT_DIRS_RELEASE} - debug ${ARCH_PREBUILT_DIRS_DEBUG} - ) - -if (NOT FMOD_LIBRARY) - set(FMOD_SDK_DIR CACHE PATH "Path to the FMOD SDK.") - if (FMOD_SDK_DIR) - find_library(FMOD_LIBRARY - NAMES fmodvc fmod-3.75 fmod - PATHS - ${FMOD_SDK_DIR}/api/lib - ${FMOD_SDK_DIR}/api - ${FMOD_SDK_DIR}/lib - ${FMOD_SDK_DIR} - ) - endif (FMOD_SDK_DIR) -endif (NOT FMOD_LIBRARY) - -find_path(FMOD_INCLUDE_DIR fmod.h - ${LIBS_PREBUILT_DIR}/include - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include - ${FMOD_SDK_DIR}/api/inc - ${FMOD_SDK_DIR}/inc - ${FMOD_SDK_DIR} - ) - -if (FMOD_LIBRARY AND FMOD_INCLUDE_DIR) - set(FMOD ON CACHE BOOL "Use closed source FMOD sound library.") -else (FMOD_LIBRARY AND FMOD_INCLUDE_DIR) - set(FMOD_LIBRARY "") - set(FMOD_INCLUDE_DIR "") - if (FMOD) - message(STATUS "No support for FMOD audio (need to set FMOD_SDK_DIR?)") - endif (FMOD) - set(FMOD OFF CACHE BOOL "Use closed source FMOD sound library.") -endif (FMOD_LIBRARY AND FMOD_INCLUDE_DIR) - -if (FMOD) - message(STATUS "Building with FMOD audio support") -endif (FMOD) diff --git a/indra/cmake/FMODEX.cmake b/indra/cmake/FMODEX.cmake index f3425f1b2..7a7bdc5ac 100644 --- a/indra/cmake/FMODEX.cmake +++ b/indra/cmake/FMODEX.cmake @@ -2,6 +2,10 @@ include(Linking) +if (FMODEX AND FMODSTUDIO) + message( FATAL_ERROR "You can not enable two FMOD variants at the same time." ) +endif (FMODEX AND FMODSTUDIO) + if (NOT FMODEX_LIBRARY) set(FMODEX_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.") if (FMODEX_SDK_DIR) diff --git a/indra/cmake/FMODSTUDIO.cmake b/indra/cmake/FMODSTUDIO.cmake new file mode 100644 index 000000000..b2afae207 --- /dev/null +++ b/indra/cmake/FMODSTUDIO.cmake @@ -0,0 +1,83 @@ +# -*- cmake -*- + +include(Linking) + +if (FMODEX AND FMODSTUDIO) + message( FATAL_ERROR "You can not enable two FMOD variants at the same time." ) +endif (FMODEX AND FMODSTUDIO) + +if (NOT FMODSTUDIO_LIBRARY) + set(FMODSTUDIO_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.") + if (FMODSTUDIO_SDK_DIR) + if(WORD_SIZE EQUAL 32) + find_library(FMODSTUDIO_LIBRARY + fmod_vc fmodL_vc fmod fmodL + PATHS + "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib" + "${FMODSTUDIO_SDK_DIR}/api/lowlevel" + "${FMODSTUDIO_SDK_DIR}" + ) + elseif(WORD_SIZE EQUAL 64) + find_library(FMODSTUDIO_LIBRARY + fmod64_vc fmodL64_vc fmod64 fmodL64 + PATHS + "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib" + "${FMODSTUDIO_SDK_DIR}/api/lowlevel" + "${FMODSTUDIO_SDK_DIR}" + ) + endif(WORD_SIZE EQUAL 32) + endif(FMODSTUDIO_SDK_DIR) + if(WINDOWS AND NOT FMODSTUDIO_SDK_DIR) + GET_FILENAME_COMPONENT(FMODSTUDIO_PROG_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Studio\ API\ Windows] ABSOLUTE CACHE) + if(WORD_SIZE EQUAL 32) + find_library(FMODSTUDIO_LIBRARY + fmod_vc fmodL_vc + PATHS + "${FMODSTUDIO_PROG_DIR}/api/lowlevel/lib" + "${FMODSTUDIO_PROG_DIR}/api/lowlevel" + "${FMODSTUDIO_PROG_DIR}" + ) + else(WORD_SIZE EQUAL 32) + find_library(FMODSTUDIO_LIBRARY + fmod64_vc fmodL64_vc + PATHS + "${FMODSTUDIO_PROG_DIR}/api/lowlevel/lib" + "${FMODSTUDIO_PROG_DIR}/api/lowlevel" + "${FMODSTUDIO_PROG_DIR}" + ) + endif(WORD_SIZE EQUAL 32) + if(FMODSTUDIO_LIBRARY) + message(STATUS "Found fmodstudio in ${FMODSTUDIO_PROG_DIR}") + set(FMODSTUDIO_SDK_DIR "${FMODSTUDIO_PROG_DIR}") + set(FMODSTUDIO_SDK_DIR "${FMODSTUDIO_PROG_DIR}" CACHE PATH "Path to the FMOD Studio SDK." FORCE) + endif(FMODSTUDIO_LIBRARY) + endif(WINDOWS AND NOT FMODSTUDIO_SDK_DIR) +endif (NOT FMODSTUDIO_LIBRARY) + +find_path(FMODSTUDIO_INCLUDE_DIR fmod.hpp + "${LIBS_PREBUILT_DIR}/include/fmodstudio" + "${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/fmodstudio" + "${FMODSTUDIO_SDK_DIR}/api/lowlevel/inc" + "${FMODSTUDIO_SDK_DIR}" + ) + +if(DARWIN) + set(FMODSTUDIO_ORIG_LIBRARY "${FMODSTUDIO_LIBRARY}") + set(FMODSTUDIO_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/libfmod.dylib") +endif(DARWIN) + +if (FMODSTUDIO_LIBRARY AND FMODSTUDIO_INCLUDE_DIR) + set(FMODSTUDIO ON CACHE BOOL "Use closed source FMOD Studio sound library.") +else (FMODSTUDIO_LIBRARY AND FMODSTUDIO_INCLUDE_DIR) + set(FMODSTUDIO_LIBRARY "") + set(FMODSTUDIO_INCLUDE_DIR "") + if (FMODSTUDIO) + message(STATUS "No support for FMOD Studio audio (need to set FMODSTUDIO_SDK_DIR?)") + endif (FMODSTUDIO) + set(FMODSTUDIO OFF CACHE BOOL "Use closed source FMOD Studio sound library.") + set(FMODSTUDIO OFF) +endif (FMODSTUDIO_LIBRARY AND FMODSTUDIO_INCLUDE_DIR) + +if (FMODSTUDIO) + message(STATUS "Building with FMOD Studio audio support") +endif (FMODSTUDIO) diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt index 22d814e03..943bd9f03 100644 --- a/indra/llaudio/CMakeLists.txt +++ b/indra/llaudio/CMakeLists.txt @@ -5,25 +5,25 @@ project(llaudio) include(00-Common) include(Audio) include(LLAudio) +if (FMODSTUDIO) + include(FMODSTUDIO) +endif (FMODSTUDIO) if (FMODEX) include(FMODEX) - set(FMOD OFF) endif (FMODEX) -if (NOT FMODEX) - include(FMOD) -endif (NOT FMODEX) include(OPENAL) include(LLCommon) include(LLMath) include(LLMessage) include(LLVFS) +if (FMODSTUDIO) + include_directories(${FMODSTUDIO_INCLUDE_DIR}) +endif(FMODSTUDIO) + if (FMODEX) include_directories(${FMODEX_INCLUDE_DIR}) endif(FMODEX) -if(FMOD) - include_directories(${FMOD_INCLUDE_DIR}) -endif (FMOD) include_directories( ${LLAUDIO_INCLUDE_DIRS} @@ -58,6 +58,20 @@ set(llaudio_HEADER_FILES llwindgen.h ) +if (FMODSTUDIO) + list(APPEND llaudio_SOURCE_FILES + llaudioengine_fmodstudio.cpp + lllistener_fmodstudio.cpp + llstreamingaudio_fmodstudio.cpp + ) + + list(APPEND llaudio_HEADER_FILES + llaudioengine_fmodstudio.h + lllistener_fmodstudio.h + llstreamingaudio_fmodstudio.h + ) +endif (FMODSTUDIO) + if (FMODEX) list(APPEND llaudio_SOURCE_FILES llaudioengine_fmodex.cpp @@ -71,26 +85,6 @@ if (FMODEX) llstreamingaudio_fmodex.h ) endif (FMODEX) -if (FMOD) - list(APPEND llaudio_SOURCE_FILES - llaudioengine_fmod.cpp - lllistener_fmod.cpp - llstreamingaudio_fmod.cpp - ) - - list(APPEND llaudio_HEADER_FILES - llaudioengine_fmod.h - lllistener_fmod.h - llstreamingaudio_fmod.h - ) - - if (LINUX) - if (${CXX_VERSION} MATCHES "4.[23]") - set_source_files_properties(llaudioengine_fmod.cpp - COMPILE_FLAGS -Wno-error=write-strings) - endif (${CXX_VERSION} MATCHES "4.[23]") - endif (LINUX) -endif (FMOD) if (OPENAL) list(APPEND llaudio_SOURCE_FILES diff --git a/indra/llaudio/llaudioengine_fmod.cpp b/indra/llaudio/llaudioengine_fmod.cpp deleted file mode 100644 index 01beaa9d5..000000000 --- a/indra/llaudio/llaudioengine_fmod.cpp +++ /dev/null @@ -1,789 +0,0 @@ -/** - * @file audioengine_fmod.cpp - * @brief Implementation of LLAudioEngine class abstracting the audio support as a FMOD 3D implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llstreamingaudio.h" -#include "llstreamingaudio_fmod.h" - -#include "llaudioengine_fmod.h" -#include "lllistener_fmod.h" - -#include "llerror.h" -#include "llmath.h" -#include "llrand.h" - -#include "fmod.h" -#include "fmod_errors.h" -#include "lldir.h" -#include "llapr.h" - -#include "sound_ids.h" - - -extern "C" { - void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata); -} - - -LLAudioEngine_FMOD::LLAudioEngine_FMOD() -{ - mInited = false; - mWindGen = NULL; - mWindDSP = NULL; -} - - -LLAudioEngine_FMOD::~LLAudioEngine_FMOD() -{ -} - - -bool LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata) -{ - LLAudioEngine::init(num_channels, userdata); - - // Reserve one extra channel for the http stream. - if (!FSOUND_SetMinHardwareChannels(num_channels + 1)) - { - LL_WARNS("AppInit") << "FMOD::init[0](), error: " << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; - } - - LL_DEBUGS("AppInit") << "LLAudioEngine_FMOD::init() initializing FMOD" << LL_ENDL; - - F32 version = FSOUND_GetVersion(); - if (version < FMOD_VERSION) - { - LL_WARNS("AppInit") << "Error : You are using the wrong FMOD version (" << version - << ")! You should be using FMOD " << FMOD_VERSION << LL_ENDL; - //return false; - } - - U32 fmod_flags = 0x0; - -#if LL_WINDOWS - // Windows needs to know which window is frontmost. - // This must be called before FSOUND_Init() per the FMOD docs. - // This could be used to let FMOD handle muting when we lose focus, - // but we don't actually want to do that because we want to distinguish - // between minimized and not-focused states. - if (!FSOUND_SetHWND(userdata)) - { - LL_WARNS("AppInit") << "Error setting FMOD window: " - << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; - return false; - } - // Play audio when we don't have focus. - // (For example, IM client on top of us.) - // This means we also try to play audio when minimized, - // so we manually handle muting in that case. JC - fmod_flags |= FSOUND_INIT_GLOBALFOCUS; - fmod_flags |= FSOUND_INIT_DSOUND_HRTF_FULL; -#endif - -#if LL_LINUX - // initialize the FMOD engine - - // This is a hack to use only FMOD's basic FPU mixer - // when the LL_VALGRIND environmental variable is set, - // otherwise valgrind will fall over on FMOD's MMX detection - if (getenv("LL_VALGRIND")) /*Flawfinder: ignore*/ - { - LL_INFOS("AppInit") << "Pacifying valgrind in FMOD init." << LL_ENDL; - FSOUND_SetMixer(FSOUND_MIXER_QUALITY_FPU); - } - - // If we don't set an output method, Linux FMOD always - // decides on OSS and fails otherwise. So we'll manually - // try ESD, then OSS, then ALSA. - // Why this order? See SL-13250, but in short, OSS emulated - // on top of ALSA is ironically more reliable than raw ALSA. - // Ack, and ESD has more reliable failure modes - but has worse - // latency - than all of them, so wins for now. - bool audio_ok = false; - - if (!audio_ok) - { - if (NULL == getenv("LL_BAD_FMOD_ESD")) /*Flawfinder: ignore*/ - { - LL_DEBUGS("AppInit") << "Trying ESD audio output..." << LL_ENDL; - if(FSOUND_SetOutput(FSOUND_OUTPUT_ESD) && - FSOUND_Init(44100, num_channels, fmod_flags)) - { - LL_DEBUGS("AppInit") << "ESD audio output initialized OKAY" - << LL_ENDL; - audio_ok = true; - } else { - LL_WARNS("AppInit") << "ESD audio output FAILED to initialize: " - << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; - } - } else { - LL_DEBUGS("AppInit") << "ESD audio output SKIPPED" << LL_ENDL; - } - } - if (!audio_ok) - { - if (NULL == getenv("LL_BAD_FMOD_OSS")) /*Flawfinder: ignore*/ - { - LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL; - if(FSOUND_SetOutput(FSOUND_OUTPUT_OSS) && - FSOUND_Init(44100, num_channels, fmod_flags)) - { - LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL; - audio_ok = true; - } else { - LL_WARNS("AppInit") << "OSS audio output FAILED to initialize: " - << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; - } - } else { - LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL; - } - } - if (!audio_ok) - { - if (NULL == getenv("LL_BAD_FMOD_ALSA")) /*Flawfinder: ignore*/ - { - LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL; - if(FSOUND_SetOutput(FSOUND_OUTPUT_ALSA) && - FSOUND_Init(44100, num_channels, fmod_flags)) - { - LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL; - audio_ok = true; - } else { - LL_WARNS("AppInit") << "ALSA audio output FAILED to initialize: " - << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; - } - } else { - LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL; - } - } - if (!audio_ok) - { - LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL; - return false; - } - - // On Linux, FMOD causes a SIGPIPE for some netstream error - // conditions (an FMOD bug); ignore SIGPIPE so it doesn't crash us. - // NOW FIXED in FMOD 3.x since 2006-10-01. - //signal(SIGPIPE, SIG_IGN); - - // We're interested in logging which output method we - // ended up with, for QA purposes. - switch (FSOUND_GetOutput()) - { - case FSOUND_OUTPUT_NOSOUND: LL_DEBUGS("AppInit") << "Audio output: NoSound" << LL_ENDL; break; - case FSOUND_OUTPUT_OSS: LL_DEBUGS("AppInit") << "Audio output: OSS" << LL_ENDL; break; - case FSOUND_OUTPUT_ESD: LL_DEBUGS("AppInit") << "Audio output: ESD" << LL_ENDL; break; - case FSOUND_OUTPUT_ALSA: LL_DEBUGS("AppInit") << "Audio output: ALSA" << LL_ENDL; break; - default: LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break; - }; - -#else // LL_LINUX - - // initialize the FMOD engine - if (!FSOUND_Init(44100, num_channels, fmod_flags)) - { - LL_WARNS("AppInit") << "Error initializing FMOD: " - << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; - return false; - } - -#endif - - // set up our favourite FMOD-native streaming audio implementation if none has already been added - if (!getStreamingAudioImpl()) // no existing implementation added - setStreamingAudioImpl(new LLStreamingAudio_FMOD()); - - LL_DEBUGS("AppInit") << "LLAudioEngine_FMOD::init() FMOD initialized correctly" << LL_ENDL; - - mInited = true; - - return true; -} - - -std::string LLAudioEngine_FMOD::getDriverName(bool verbose) -{ - if (verbose) - { - F32 version = FSOUND_GetVersion(); - return llformat("FMOD version %f", version); - } - else - { - return "FMOD"; - } -} - - -void LLAudioEngine_FMOD::allocateListener(void) -{ - mListenerp = (LLListener *) new LLListener_FMOD(); - if (!mListenerp) - { - llwarns << "Listener creation failed" << llendl; - } -} - - -void LLAudioEngine_FMOD::shutdown() -{ - if (mWindDSP) - { - FSOUND_DSP_SetActive(mWindDSP,false); - FSOUND_DSP_Free(mWindDSP); - } - - stopInternetStream(); - - LLAudioEngine::shutdown(); - - llinfos << "LLAudioEngine_FMOD::shutdown() closing FMOD" << llendl; - FSOUND_Close(); - llinfos << "LLAudioEngine_FMOD::shutdown() done closing FMOD" << llendl; - - delete mListenerp; - mListenerp = NULL; -} - - -LLAudioBuffer * LLAudioEngine_FMOD::createBuffer() -{ - return new LLAudioBufferFMOD(); -} - - -LLAudioChannel * LLAudioEngine_FMOD::createChannel() -{ - return new LLAudioChannelFMOD(); -} - - -bool LLAudioEngine_FMOD::initWind() -{ - if (!mWindGen) - { - bool enable; - - switch (FSOUND_GetMixer()) - { - case FSOUND_MIXER_MMXP5: - case FSOUND_MIXER_MMXP6: - case FSOUND_MIXER_QUALITY_MMXP5: - case FSOUND_MIXER_QUALITY_MMXP6: - enable = (typeid(MIXBUFFERFORMAT) == typeid(S16)); - break; - case FSOUND_MIXER_BLENDMODE: - enable = (typeid(MIXBUFFERFORMAT) == typeid(S32)); - break; - case FSOUND_MIXER_QUALITY_FPU: - enable = (typeid(MIXBUFFERFORMAT) == typeid(F32)); - break; - default: - // FSOUND_GetMixer() does not return a valid mixer type on Darwin - LL_INFOS("AppInit") << "Unknown FMOD mixer type, assuming default" << LL_ENDL; - enable = true; - break; - } - - if (enable) - { - mWindGen = new LLWindGen(FSOUND_GetOutputRate()); - } - else - { - LL_WARNS("AppInit") << "Incompatible FMOD mixer type, wind noise disabled" << LL_ENDL; - } - } - - mNextWindUpdate = 0.0; - - if (mWindGen && !mWindDSP) - { - mWindDSP = FSOUND_DSP_Create(&windCallback, FSOUND_DSP_DEFAULTPRIORITY_CLEARUNIT + 20, mWindGen); - } - if (mWindDSP) - { - FSOUND_DSP_SetActive(mWindDSP, true); - return true; - } - - return false; -} - - -void LLAudioEngine_FMOD::cleanupWind() -{ - if (mWindDSP) - { - FSOUND_DSP_SetActive(mWindDSP, false); - FSOUND_DSP_Free(mWindDSP); - mWindDSP = NULL; - } - - delete mWindGen; - mWindGen = NULL; -} - - -//----------------------------------------------------------------------- -void LLAudioEngine_FMOD::updateWind(LLVector3 wind_vec, F32 camera_height_above_water) -{ - LLVector3 wind_pos; - F64 pitch; - F64 center_freq; - - if (!mEnableWind) - { - return; - } - - if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL)) - { - - // wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up) - // need to convert this to the conventional orientation DS3D and OpenAL use - // where +X = right, +Y = up, +Z = backwards - - wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]); - - // cerr << "Wind update" << endl; - - pitch = 1.0 + mapWindVecToPitch(wind_vec); - center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0)); - - mWindGen->mTargetFreq = (F32)center_freq; - mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain; - mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec); - } -} - -/* -//----------------------------------------------------------------------- -void LLAudioEngine_FMOD::setSourceMinDistance(U16 source_num, F64 distance) -{ - if (!mInited) - { - return; - } - if (mBuffer[source_num]) - { - mMinDistance[source_num] = (F32) distance; - if (!FSOUND_Sample_SetMinMaxDistance(mBuffer[source_num],mMinDistance[source_num], mMaxDistance[source_num])) - { - llwarns << "FMOD::setSourceMinDistance(" << source_num << "), error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; - } - } -} - -//----------------------------------------------------------------------- -void LLAudioEngine_FMOD::setSourceMaxDistance(U16 source_num, F64 distance) -{ - if (!mInited) - { - return; - } - if (mBuffer[source_num]) - { - mMaxDistance[source_num] = (F32) distance; - if (!FSOUND_Sample_SetMinMaxDistance(mBuffer[source_num],mMinDistance[source_num], mMaxDistance[source_num])) - { - llwarns << "FMOD::setSourceMaxDistance(" << source_num << "), error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; - } - } -} - -//----------------------------------------------------------------------- -void LLAudioEngine_FMOD::get3DParams(S32 source_num, S32 *volume, S32 *freq, S32 *inside, S32 *outside, LLVector3 *orient, S32 *out_volume, F32 *min_dist, F32 *max_dist) -{ - *volume = 0; - *freq = 0; - *inside = 0; - *outside = 0; - *orient = LLVector3::zero; - *out_volume = 0; - *min_dist = 0.f; - *max_dist = 0.f; -} - -*/ - - -//----------------------------------------------------------------------- -void LLAudioEngine_FMOD::setInternalGain(F32 gain) -{ - if (!mInited) - { - return; - } - - gain = llclamp( gain, 0.0f, 1.0f ); - FSOUND_SetSFXMasterVolume( llround( 255.0f * gain ) ); - - LLStreamingAudioInterface *saimpl = getStreamingAudioImpl(); - if ( saimpl ) - { - // fmod likes its streaming audio channel gain re-asserted after - // master volume change. - saimpl->setGain(saimpl->getGain()); - } -} - -// -// LLAudioChannelFMOD implementation -// - -LLAudioChannelFMOD::LLAudioChannelFMOD() : LLAudioChannel(), mChannelID(0), mLastSamplePos(0) -{ -} - - -LLAudioChannelFMOD::~LLAudioChannelFMOD() -{ - cleanup(); -} - - -bool LLAudioChannelFMOD::updateBuffer() -{ - if (LLAudioChannel::updateBuffer()) - { - // Base class update returned true, which means that we need to actually - // set up the channel for a different buffer. - - LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)mCurrentSourcep->getCurrentBuffer(); - - // Grab the FMOD sample associated with the buffer - FSOUND_SAMPLE *samplep = bufferp->getSample(); - if (!samplep) - { - // This is bad, there should ALWAYS be a sample associated with a legit - // buffer. - llerrs << "No FMOD sample!" << llendl; - return false; - } - - - // Actually play the sound. Start it off paused so we can do all the necessary - // setup. - mChannelID = FSOUND_PlaySoundEx(FSOUND_FREE, samplep, FSOUND_DSP_GetSFXUnit(), true); - - //llinfos << "Setting up channel " << std::hex << mChannelID << std::dec << llendl; - } - - // If we have a source for the channel, we need to update its gain. - if (mCurrentSourcep) - { - // SJB: warnings can spam and hurt framerate, disabling - if (!FSOUND_SetVolume(mChannelID, llround(getSecondaryGain() * mCurrentSourcep->getGain() * 255.0f))) - { -// llwarns << "LLAudioChannelFMOD::updateBuffer error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; - } - - if (!FSOUND_SetLoopMode(mChannelID, mCurrentSourcep->isLoop() ? FSOUND_LOOP_NORMAL : FSOUND_LOOP_OFF)) - { -// llwarns << "Channel " << mChannelID << "Source ID: " << mCurrentSourcep->getID() -// << " at " << mCurrentSourcep->getPositionGlobal() << llendl; -// llwarns << "LLAudioChannelFMOD::updateBuffer error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; - } - } - - return true; -} - - -void LLAudioChannelFMOD::update3DPosition() -{ - if (!mChannelID) - { - // We're not actually a live channel (i.e., we're not playing back anything) - return; - } - - LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)mCurrentBufferp; - if (!bufferp) - { - // We don't have a buffer associated with us (should really have been picked up - // by the above if. - return; - } - - if (mCurrentSourcep->isAmbient()) - { - // Ambient sound, don't need to do any positional updates. - bufferp->set3DMode(false); - } - else - { - // Localized sound. Update the position and velocity of the sound. - bufferp->set3DMode(true); - - LLVector3 float_pos; - float_pos.setVec(mCurrentSourcep->getPositionGlobal()); - if (!FSOUND_3D_SetAttributes(mChannelID, float_pos.mV, mCurrentSourcep->getVelocity().mV)) - { - LL_DEBUGS("FMOD") << "LLAudioChannelFMOD::update3DPosition error: " << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; - } - } -} - - -void LLAudioChannelFMOD::updateLoop() -{ - if (!mChannelID) - { - // May want to clear up the loop/sample counters. - return; - } - - // - // Hack: We keep track of whether we looped or not by seeing when the - // sample position looks like it's going backwards. Not reliable; may - // yield false negatives. - // - U32 cur_pos = FSOUND_GetCurrentPosition(mChannelID); - if (cur_pos < (U32)mLastSamplePos) - { - mLoopedThisFrame = true; - } - mLastSamplePos = cur_pos; -} - - -void LLAudioChannelFMOD::cleanup() -{ - LLAudioChannel::cleanup(); - if (!mChannelID) - { - //llinfos << "Aborting cleanup with no channelID." << llendl; - return; - } - - //llinfos << "Cleaning up channel: " << mChannelID << llendl; - if (!FSOUND_StopSound(mChannelID)) - { - LL_DEBUGS("FMOD") << "LLAudioChannelFMOD::cleanup error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; - } - - mChannelID = 0; - mLastSamplePos = 0; -} - - -void LLAudioChannelFMOD::play() -{ - if (!mChannelID) - { - llwarns << "Playing without a channelID, aborting" << llendl; - return; - } - - if(!FSOUND_IsPaused(mChannelID)) - { - FSOUND_SetPaused(mChannelID, true); - FSOUND_SetCurrentPosition(mChannelID, 0); - } - - if (!FSOUND_SetPaused(mChannelID, false)) - { - llwarns << "LLAudioChannelFMOD::play error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; - } - getSource()->setPlayedOnce(true); -} - - -void LLAudioChannelFMOD::playSynced(LLAudioChannel *channelp) -{ - LLAudioChannelFMOD *fmod_channelp = (LLAudioChannelFMOD*)channelp; - if (!(fmod_channelp->mChannelID && mChannelID)) - { - // Don't have channels allocated to both the master and the slave - return; - } - - U32 position = FSOUND_GetCurrentPosition(fmod_channelp->mChannelID) % mCurrentBufferp->getLength(); - // Try to match the position of our sync master - if (!FSOUND_SetCurrentPosition(mChannelID, position)) - { - llwarns << "LLAudioChannelFMOD::playSynced unable to set current position" << llendl; - } - - // Start us playing - play(); -} - - -bool LLAudioChannelFMOD::isPlaying() -{ - if (!mChannelID) - { - return false; - } - - return FSOUND_IsPlaying(mChannelID) && (!FSOUND_GetPaused(mChannelID)); -} - - - -// -// LLAudioBufferFMOD implementation -// - - -LLAudioBufferFMOD::LLAudioBufferFMOD() : LLAudioBuffer() -{ - mSamplep = NULL; -} - - -LLAudioBufferFMOD::~LLAudioBufferFMOD() -{ - if (mSamplep) - { - // Clean up the associated FMOD sample if it exists. - FSOUND_Sample_Free(mSamplep); - mSamplep = NULL; - } -} - - -bool LLAudioBufferFMOD::loadWAV(const std::string& filename) -{ - // Try to open a wav file from disk. This will eventually go away, as we don't - // really want to block doing this. - if (filename.empty()) - { - // invalid filename, abort. - return false; - } - - if (!LLAPRFile::isExist(filename, LL_APR_RPB)) - { - // File not found, abort. - return false; - } - - if (mSamplep) - { - // If there's already something loaded in this buffer, clean it up. - FSOUND_Sample_Free(mSamplep); - mSamplep = NULL; - } - - // Load up the wav file into an fmod sample -#if LL_WINDOWS - // MikeS. - Loading the sound file manually and then handing it over to FMOD, - // since FMOD uses posix IO internally, - // which doesn't work with unicode file paths. - LLFILE* sound_file = LLFile::fopen(filename,"rb"); /* Flawfinder: ignore */ - if (sound_file) - { - fseek(sound_file,0,SEEK_END); - U32 file_length = ftell(sound_file); //Find the length of the file by seeking to the end and getting the offset - size_t read_count; - fseek(sound_file,0,SEEK_SET); //Seek back to the beginning - char* buffer = new char[file_length]; - llassert(buffer); - read_count = fread((void*)buffer,file_length,1,sound_file);//Load it.. - if(ferror(sound_file)==0 && (read_count == 1)){//No read error, and we got 1 chunk of our size... - unsigned int mode_flags = FSOUND_LOOP_NORMAL | FSOUND_LOADMEMORY; - //FSOUND_16BITS | FSOUND_MONO | FSOUND_LOADMEMORY | FSOUND_LOOP_NORMAL; - mSamplep = FSOUND_Sample_Load(FSOUND_UNMANAGED, buffer, mode_flags , 0, file_length); - } - delete[] buffer; - fclose(sound_file); - } -#else - mSamplep = FSOUND_Sample_Load(FSOUND_UNMANAGED, filename.c_str(), FSOUND_LOOP_NORMAL, 0, 0); -#endif - - if (!mSamplep) - { - // We failed to load the file for some reason. - llwarns << "Could not load data '" << filename << "': " - << FMOD_ErrorString(FSOUND_GetError()) << llendl; - - // - // If we EVER want to load wav files provided by end users, we need - // to rethink this! - // - // file is probably corrupt - remove it. - LLFile::remove(filename); - return false; - } - - // Everything went well, return true - return true; -} - - -U32 LLAudioBufferFMOD::getLength() -{ - if (!mSamplep) - { - return 0; - } - - return FSOUND_Sample_GetLength(mSamplep); -} - - -void LLAudioBufferFMOD::set3DMode(bool use3d) -{ - U16 current_mode = FSOUND_Sample_GetMode(mSamplep); - - if (use3d) - { - if (!FSOUND_Sample_SetMode(mSamplep, (current_mode & (~FSOUND_2D)))) - { - llwarns << "LLAudioBufferFMOD::set3DMode error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; - } - } - else - { - if (!FSOUND_Sample_SetMode(mSamplep, current_mode | FSOUND_2D)) - { - llwarns << "LLAudioBufferFMOD::set3DMode error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; - } - } -} - - -void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata) -{ - // originalbuffer = fmod's original mixbuffer. - // newbuffer = the buffer passed from the previous DSP unit. - // length = length in samples at this mix time. - // userdata = user parameter passed through in FSOUND_DSP_Create. - - LLWindGen *windgen = - (LLWindGen *)userdata; - - newbuffer = windgen->windGenerate((LLAudioEngine_FMOD::MIXBUFFERFORMAT *)newbuffer, length); - - return newbuffer; -} diff --git a/indra/llaudio/llaudioengine_fmod.h b/indra/llaudio/llaudioengine_fmod.h deleted file mode 100644 index 4582a5d57..000000000 --- a/indra/llaudio/llaudioengine_fmod.h +++ /dev/null @@ -1,124 +0,0 @@ -/** - * @file audioengine_fmod.h - * @brief Definition of LLAudioEngine class abstracting the audio - * support as a FMOD 3D implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_AUDIOENGINE_FMOD_H -#define LL_AUDIOENGINE_FMOD_H - -#include "llaudioengine.h" -#include "lllistener_fmod.h" -#include "llwindgen.h" - -#include "fmod.h" - -class LLAudioStreamManagerFMOD; - -class LLAudioEngine_FMOD : public LLAudioEngine -{ -public: - LLAudioEngine_FMOD(); - virtual ~LLAudioEngine_FMOD(); - - // initialization/startup/shutdown - virtual bool init(const S32 num_channels, void *user_data); - virtual std::string getDriverName(bool verbose); - virtual void allocateListener(); - - virtual void shutdown(); - - /*virtual*/ bool initWind(); - /*virtual*/ void cleanupWind(); - - /*virtual*/void updateWind(LLVector3 direction, F32 camera_height_above_water); - -#if LL_DARWIN - typedef S32 MIXBUFFERFORMAT; -#else - typedef S16 MIXBUFFERFORMAT; -#endif - -protected: - /*virtual*/ LLAudioBuffer *createBuffer(); // Get a free buffer, or flush an existing one if you have to. - /*virtual*/ LLAudioChannel *createChannel(); // Create a new audio channel. - - /*virtual*/ void setInternalGain(F32 gain); -protected: - static signed char F_CALLBACKAPI callbackMetaData(char* name, char* value, void* userdata); - - //F32 mMinDistance[MAX_BUFFERS]; - //F32 mMaxDistance[MAX_BUFFERS]; - - bool mInited; - - // On Windows, userdata is the HWND of the application window. - void* mUserData; - - LLWindGen *mWindGen; - FSOUND_DSPUNIT *mWindDSP; -}; - - -class LLAudioChannelFMOD : public LLAudioChannel -{ -public: - LLAudioChannelFMOD(); - virtual ~LLAudioChannelFMOD(); - -protected: - /*virtual*/ void play(); - /*virtual*/ void playSynced(LLAudioChannel *channelp); - /*virtual*/ void cleanup(); - /*virtual*/ bool isPlaying(); - - /*virtual*/ bool updateBuffer(); - /*virtual*/ void update3DPosition(); - /*virtual*/ void updateLoop(); - -protected: - int mChannelID; - S32 mLastSamplePos; -}; - - -class LLAudioBufferFMOD : public LLAudioBuffer -{ -public: - LLAudioBufferFMOD(); - virtual ~LLAudioBufferFMOD(); - - /*virtual*/ bool loadWAV(const std::string& filename); - /*virtual*/ U32 getLength(); - friend class LLAudioChannelFMOD; - - void set3DMode(bool use3d); -protected: - FSOUND_SAMPLE *getSample() { return mSamplep; } -protected: - FSOUND_SAMPLE *mSamplep; -}; - - -#endif // LL_AUDIOENGINE_FMOD_H diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp new file mode 100644 index 000000000..9b5ccebc0 --- /dev/null +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -0,0 +1,1016 @@ +/** + * @file audioengine_FMODSTUDIO.cpp + * @brief Implementation of LLAudioEngine class abstracting the audio support as a FMOD 3D implementation + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llstreamingaudio.h" +#include "llstreamingaudio_fmodstudio.h" + +#include "llaudioengine_fmodstudio.h" +#include "lllistener_fmodstudio.h" + +#include "llerror.h" +#include "llmath.h" +#include "llrand.h" + +#include "fmod.hpp" +#include "fmod_errors.h" +#include "lldir.h" +#include "llapr.h" + +#include "sound_ids.h" + +#if LL_WINDOWS //Some ugly code to make missing fmod.dll not cause a fatal error. +#define WIN32_LEAN_AND_MEAN +#include "windows.h" +#include +#pragma comment(lib, "delayimp.lib") + +bool attemptDelayLoad() +{ + __try + { +#if defined(_WIN64) + if( FAILED( __HrLoadAllImportsForDll( "fmod64.dll" ) ) ) + return false; +#else + if( FAILED( __HrLoadAllImportsForDll( "fmod.dll" ) ) ) + return false; +#endif + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + return false; + } + return true; +} +#endif + +static bool sVerboseDebugging = false; + +FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + +FMOD::ChannelGroup *LLAudioEngine_FMODSTUDIO::mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT] = {0}; + +//This class is designed to keep track of all sound<->channel assocations. +//Used to verify validity of sound and channel pointers, as well as catch cases were sounds +//are released with active channels still attached. +class CFMODSoundChecks +{ + typedef std::map > active_sounds_t; + typedef std::set dead_sounds_t; + typedef std::map active_channels_t; + typedef std::map dead_channels_t; + + active_sounds_t mActiveSounds; + dead_sounds_t mDeadSounds; + active_channels_t mActiveChannels; + dead_channels_t mDeadChannels; +public: + enum STATUS + { + ACTIVE, + DEAD, + UNKNOWN + }; + STATUS getPtrStatus(LLAudioChannel* channel) + { + if(!channel) + return UNKNOWN; + return getPtrStatus(dynamic_cast(channel)->mChannelp); + } + STATUS getPtrStatus(LLAudioBuffer* sound) + { + if(!sound) + return UNKNOWN; + return getPtrStatus(dynamic_cast(sound)->mSoundp); + } + STATUS getPtrStatus(FMOD::Channel* channel) + { + if(!channel) + return UNKNOWN; + else if(mActiveChannels.find(channel) != mActiveChannels.end()) + return ACTIVE; + else if(mDeadChannels.find(channel) != mDeadChannels.end()) + return DEAD; + return UNKNOWN; + } + STATUS getPtrStatus(FMOD::Sound* sound) + { + if(!sound) + return UNKNOWN; + if(mActiveSounds.find(sound) != mActiveSounds.end()) + return ACTIVE; + else if(mDeadSounds.find(sound) != mDeadSounds.end()) + return DEAD; + return UNKNOWN; + } + void addNewSound(FMOD::Sound* sound) + { + assertActiveState(sound,true,false); + + mDeadSounds.erase(sound); + mActiveSounds.insert(std::make_pair(sound,std::set())); + } + void removeSound(FMOD::Sound* sound) + { + assertActiveState(sound,true); + + active_sounds_t::const_iterator it = mActiveSounds.find(sound); + llassert(it != mActiveSounds.end()); + if(it != mActiveSounds.end()) + { + if(!it->second.empty()) + { + LL_WARNS("AudioImpl") << "Removing sound " << sound << " with attached channels: \n"; + for(std::set::iterator it2 = it->second.begin(); it2 != it->second.end();++it2) + { + switch(getPtrStatus(*it2)) + { + case ACTIVE: + LL_CONT << " Channel " << *it2 << " ACTIVE\n"; + break; + case DEAD: + LL_CONT << " Channel " << *it2 << " DEAD\n"; + break; + default: + LL_CONT << " Channel " << *it2 << " UNKNOWN\n"; + } + } + LL_CONT << LL_ENDL; + } + llassert(it->second.empty()); + mDeadSounds.insert(sound); + mActiveSounds.erase(sound); + } + } + void addNewChannelToSound(FMOD::Sound* sound,FMOD::Channel* channel) + { + assertActiveState(sound,true); + assertActiveState(channel,true,false); + + mActiveSounds[sound].insert(channel); + mActiveChannels.insert(std::make_pair(channel,sound)); + } + void removeChannel(FMOD::Channel* channel) + { + assertActiveState(channel,true); + + active_channels_t::const_iterator it = mActiveChannels.find(channel); + llassert(it != mActiveChannels.end()); + if(it != mActiveChannels.end()) + { +#ifdef SHOW_ASSERT + STATUS status = getPtrStatus(it->second); + llassert(status != DEAD); + llassert(status != UNKNOWN); +#endif + + active_sounds_t::iterator it2 = mActiveSounds.find(it->second); + llassert(it2 != mActiveSounds.end()); + if(it2 != mActiveSounds.end()) + { + it2->second.erase(channel); + } + mDeadChannels.insert(*it); + mActiveChannels.erase(channel); + } + } + + template + void assertActiveState(T ptr, bool try_log=false, bool active=true) + { +#ifndef SHOW_ASSERT + if(try_log && sVerboseDebugging) +#endif + { + CFMODSoundChecks::STATUS chan = getPtrStatus(ptr); + if(try_log && sVerboseDebugging) + { + if(active) + { + if(chan == CFMODSoundChecks::DEAD) + LL_WARNS("AudioImpl") << __FUNCTION__ << ": Using unexpectedly dead " << typeid(T*).name() << " " << ptr << LL_ENDL; + else if(chan == CFMODSoundChecks::UNKNOWN) + LL_WARNS("AudioImpl") << __FUNCTION__ << ": Using unexpectedly unknown " << typeid(T*).name() << " " << ptr << LL_ENDL; + } + else if(chan == CFMODSoundChecks::ACTIVE) + LL_WARNS("AudioImpl") << __FUNCTION__ << ": Using unexpectedly active " << typeid(T*).name() << " " << ptr << LL_ENDL; + } + llassert( active == (chan == CFMODSoundChecks::ACTIVE) ); + } + } +} gSoundCheck; + +LLAudioEngine_FMODSTUDIO::LLAudioEngine_FMODSTUDIO(bool enable_profiler, bool verbose_debugging) +{ + sVerboseDebugging = verbose_debugging; + mInited = false; + mWindGen = NULL; + mWindDSP = NULL; + mSystem = NULL; + mEnableProfiler = enable_profiler; +} + + +LLAudioEngine_FMODSTUDIO::~LLAudioEngine_FMODSTUDIO() +{ +} + + +inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string) +{ + if(result == FMOD_OK) + return false; + LL_WARNS("AudioImpl") << string << " Error: " << FMOD_ErrorString(result) << LL_ENDL; + return true; +} + +void* F_STDCALL decode_alloc(unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr) +{ + if(type & FMOD_MEMORY_STREAM_DECODE) + { + LL_INFOS("AudioImpl") << "Decode buffer size: " << size << LL_ENDL; + } + else if(type & FMOD_MEMORY_STREAM_FILE) + { + LL_INFOS("AudioImpl") << "Stream buffer size: " << size << LL_ENDL; + } + return new char[size]; +} +void* F_STDCALL decode_realloc(void *ptr, unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr) +{ + memset(ptr,0,size); + return ptr; +} +void F_STDCALL decode_dealloc(void *ptr, FMOD_MEMORY_TYPE type, const char *sourcestr) +{ + delete[] (char*)ptr; +} + +bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) +{ + +#if LL_WINDOWS + if(!attemptDelayLoad()) + return false; +#endif + + U32 version; + FMOD_RESULT result; + + LL_DEBUGS("AppInit") << "LLAudioEngine_FMODSTUDIO::init() initializing FMOD" << LL_ENDL; + + //result = FMOD::Memory_Initialize(NULL, 0, &decode_alloc, &decode_realloc, &decode_dealloc, FMOD_MEMORY_STREAM_DECODE | FMOD_MEMORY_STREAM_FILE); + //if(Check_FMOD_Error(result, "FMOD::Memory_Initialize")) + // return false; + + result = FMOD::System_Create(&mSystem); + if(Check_FMOD_Error(result, "FMOD::System_Create")) + return false; + + //will call LLAudioEngine_FMODSTUDIO::allocateListener, which needs a valid mSystem pointer. + LLAudioEngine::init(num_channels, userdata); + + result = mSystem->getVersion(&version); + Check_FMOD_Error(result, "FMOD::System::getVersion"); + + if (version < FMOD_VERSION) + { + LL_WARNS("AppInit") << "Error : You are using the wrong FMOD Studio version (" << version + << ")! You should be using FMOD Studio" << FMOD_VERSION << LL_ENDL; + } + + // In this case, all sounds, PLUS wind and stream will be software. + result = mSystem->setSoftwareChannels(num_channels + 2); + Check_FMOD_Error(result,"FMOD::System::setSoftwareChannels"); + + U32 fmod_flags = FMOD_INIT_NORMAL; + if(mEnableProfiler) + { + fmod_flags |= FMOD_INIT_PROFILE_ENABLE; + mSystem->createChannelGroup("None", &mChannelGroups[AUDIO_TYPE_NONE]); + mSystem->createChannelGroup("SFX", &mChannelGroups[AUDIO_TYPE_SFX]); + mSystem->createChannelGroup("UI", &mChannelGroups[AUDIO_TYPE_UI]); + mSystem->createChannelGroup("Ambient", &mChannelGroups[AUDIO_TYPE_AMBIENT]); + } + +#if LL_LINUX + bool audio_ok = false; + + if (!audio_ok) + { + if (NULL == getenv("LL_BAD_FMOD_PULSEAUDIO")) /*Flawfinder: ignore*/ + { + LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL; + if(mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO) == FMOD_OK && + (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) + { + LL_DEBUGS("AppInit") << "PulseAudio output initialized OKAY" << LL_ENDL; + audio_ok = true; + } + else + { + Check_FMOD_Error(result, "PulseAudio audio output FAILED to initialize"); + } + } + else + { + LL_DEBUGS("AppInit") << "PulseAudio audio output SKIPPED" << LL_ENDL; + } + } + if (!audio_ok) + { + if (NULL == getenv("LL_BAD_FMOD_ALSA")) /*Flawfinder: ignore*/ + { + LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL; + if(mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA) == FMOD_OK && + (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) + { + LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL; + audio_ok = true; + } + else + { + Check_FMOD_Error(result, "ALSA audio output FAILED to initialize"); + } + } + else + { + LL_DEBUGS("AppInit") << "ALSA audio output SKIPPED" << LL_ENDL; + } + } + if (!audio_ok) + { + if (NULL == getenv("LL_BAD_FMOD_OSS")) /*Flawfinder: ignore*/ + { + LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL; + if(mSystem->setOutput(FMOD_OUTPUTTYPE_OSS) == FMOD_OK && + (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) + { + LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL; + audio_ok = true; + } + else + { + Check_FMOD_Error(result, "OSS audio output FAILED to initialize"); + } + } + else + { + LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL; + } + } + if (!audio_ok) + { + LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL; + return false; + } + + // We're interested in logging which output method we + // ended up with, for QA purposes. + FMOD_OUTPUTTYPE output_type; + mSystem->getOutput(&output_type); + switch (output_type) + { + case FMOD_OUTPUTTYPE_NOSOUND: + LL_INFOS("AppInit") << "Audio output: NoSound" << LL_ENDL; break; + case FMOD_OUTPUTTYPE_PULSEAUDIO: + LL_INFOS("AppInit") << "Audio output: PulseAudio" << LL_ENDL; break; + case FMOD_OUTPUTTYPE_ALSA: + LL_INFOS("AppInit") << "Audio output: ALSA" << LL_ENDL; break; + case FMOD_OUTPUTTYPE_OSS: + LL_INFOS("AppInit") << "Audio output: OSS" << LL_ENDL; break; + default: + LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break; + }; +#else // LL_LINUX + + // initialize the FMOD engine + result = mSystem->init( num_channels + 2, fmod_flags, 0); + if (result == FMOD_ERR_OUTPUT_CREATEBUFFER) + { + /* + Ok, the speaker mode selected isn't supported by this soundcard. Switch it + back to stereo... + */ + result = mSystem->setSoftwareFormat(44100, FMOD_SPEAKERMODE_STEREO, 2); + Check_FMOD_Error(result,"Error falling back to stereo mode"); + /* + ... and re-init. + */ + result = mSystem->init( num_channels + 2, fmod_flags, 0); + } + if(Check_FMOD_Error(result, "Error initializing FMOD Studio")) + return false; +#endif + + // set up our favourite FMOD-native streaming audio implementation if none has already been added + if (!getStreamingAudioImpl()) // no existing implementation added + setStreamingAudioImpl(new LLStreamingAudio_FMODSTUDIO(mSystem)); + + LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init() FMOD Studio initialized correctly" << LL_ENDL; + + int r_numbuffers, r_samplerate; + unsigned int r_bufferlength; + char r_name[256]; + FMOD_SPEAKERMODE speaker_mode; + mSystem->getDSPBufferSize(&r_bufferlength, &r_numbuffers); + mSystem->getSoftwareFormat(&r_samplerate, &speaker_mode, NULL); + mSystem->getDriverInfo(0, r_name, NULL, 255, NULL, NULL, &speaker_mode, NULL); + std::string speaker_mode_str = "unknown"; + switch(speaker_mode) + { + #define SPEAKER_MODE_CASE(MODE) case MODE: speaker_mode_str = #MODE; break; + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_RAW) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_MONO) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_STEREO) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_QUAD) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_SURROUND) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_5POINT1) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_7POINT1) + default:; + #undef SPEAKER_MODE_CASE + } + + r_name[255] = '\0'; + int latency = 1000.0 * r_bufferlength * r_numbuffers /r_samplerate; + + LL_INFOS("AppInit") << "FMOD device: "<< r_name << "\n" + << "Output mode: "<< speaker_mode_str << "\n" + << "FMOD Studio parameters: " << r_samplerate << " Hz * " <<" bit\n" + << "\tbuffer " << r_bufferlength << " * " << r_numbuffers << " (" << latency <<"ms)" << LL_ENDL; + + mInited = true; + + return true; +} + + +std::string LLAudioEngine_FMODSTUDIO::getDriverName(bool verbose) +{ + llassert_always(mSystem); + if (verbose) + { + U32 version; + if(!Check_FMOD_Error(mSystem->getVersion(&version), "FMOD::System::getVersion")) + { + return llformat("FMOD Studio %1x.%02x.%02x", version >> 16, version >> 8 & 0x000000FF, version & 0x000000FF); + } + } + return "FMODEx"; +} + + +void LLAudioEngine_FMODSTUDIO::allocateListener(void) +{ + mListenerp = (LLListener *) new LLListener_FMODSTUDIO(mSystem); + if (!mListenerp) + { + LL_WARNS("AudioImpl") << "Listener creation failed" << LL_ENDL; + } +} + + +void LLAudioEngine_FMODSTUDIO::shutdown() +{ + stopInternetStream(); + + LL_INFOS("AudioImpl") << "About to LLAudioEngine::shutdown()" << LL_ENDL; + LLAudioEngine::shutdown(); + + LL_INFOS("AudioImpl") << "LLAudioEngine_FMODSTUDIO::shutdown() closing FMOD Studio" << LL_ENDL; + if ( mSystem ) // speculative fix for MAINT-2657 + { + LL_INFOS("AudioImpl") << "LLAudioEngine_FMODSTUDIO::shutdown() Requesting FMOD Studio system closure" << LL_ENDL; + mSystem->close(); + LL_INFOS("AudioImpl") << "LLAudioEngine_FMODSTUDIO::shutdown() Requesting FMOD Studio system release" << LL_ENDL; + mSystem->release(); + } + LL_INFOS("AudioImpl") << "LLAudioEngine_FMODSTUDIO::shutdown() done closing FMOD Studio" << LL_ENDL; + + delete mListenerp; + mListenerp = NULL; +} + + +LLAudioBuffer * LLAudioEngine_FMODSTUDIO::createBuffer() +{ + return new LLAudioBufferFMODSTUDIO(mSystem); +} + + +LLAudioChannel * LLAudioEngine_FMODSTUDIO::createChannel() +{ + return new LLAudioChannelFMODSTUDIO(mSystem); +} + +bool LLAudioEngine_FMODSTUDIO::initWind() +{ + //mNextWindUpdate = 0.0; + + //if (!mWindDSP) + //{ + // FMOD_DSP_DESCRIPTION dspdesc; + // memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION)); //Set everything to zero + // strncpy(dspdesc.name,"Wind Unit", sizeof(dspdesc.name)); //Set name to "Wind Unit" + // dspdesc.read = &windCallback; //Assign callback. + // if(Check_FMOD_Error(mSystem->createDSP(&dspdesc, &mWindDSP), "FMOD::createDSP")) + // return false; + + // if(mWindGen) + // delete mWindGen; + // + // int frequency = 44100; + // mSystem->getSoftwareFormat(&frequency, NULL, NULL); + // mWindGen = new LLWindGen((U32)frequency); + // mWindDSP->setUserData((void*)mWindGen); + //} + + //if (mWindDSP) + //{ + // mSystem->playDSP(mWindDSP, NULL, false, 0); + // return true; + //} + return false; +} + + +void LLAudioEngine_FMODSTUDIO::cleanupWind() +{ + //if (mWindDSP) + //{ + // mWindDSP->remove(); + // mWindDSP->release(); + // mWindDSP = NULL; + //} + + //delete mWindGen; + //mWindGen = NULL; +} + + +//----------------------------------------------------------------------- +void LLAudioEngine_FMODSTUDIO::updateWind(LLVector3 wind_vec, F32 camera_height_above_water) +{ + LLVector3 wind_pos; + F64 pitch; + F64 center_freq; + + if (!mEnableWind) + { + return; + } + + if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL)) + { + + // wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up) + // need to convert this to the conventional orientation DS3D and OpenAL use + // where +X = right, +Y = up, +Z = backwards + + wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]); + + // cerr << "Wind update" << endl; + + pitch = 1.0 + mapWindVecToPitch(wind_vec); + center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0)); + + mWindGen->mTargetFreq = (F32)center_freq; + mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain; + mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec); + } +} + +//----------------------------------------------------------------------- +void LLAudioEngine_FMODSTUDIO::setInternalGain(F32 gain) +{ + if (!mInited) + { + return; + } + + gain = llclamp( gain, 0.0f, 1.0f ); + + FMOD::ChannelGroup *master_group; + mSystem->getMasterChannelGroup(&master_group); + + master_group->setVolume(gain); + + LLStreamingAudioInterface *saimpl = getStreamingAudioImpl(); + if ( saimpl ) + { + // fmod likes its streaming audio channel gain re-asserted after + // master volume change. + saimpl->setGain(saimpl->getGain()); + } +} + +// +// LLAudioChannelFMODSTUDIO implementation +// + +LLAudioChannelFMODSTUDIO::LLAudioChannelFMODSTUDIO(FMOD::System *system) : LLAudioChannel(), mSystemp(system), mChannelp(NULL), mLastSamplePos(0) +{ +} + + +LLAudioChannelFMODSTUDIO::~LLAudioChannelFMODSTUDIO() +{ + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Destructing Audio Channel. mChannelp = " << mChannelp << LL_ENDL; + + cleanup(); +} + +static FMOD_RESULT F_CALLBACK channel_callback(FMOD_CHANNELCONTROL *channel, FMOD_CHANNELCONTROL_TYPE controltype, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbacktype, void *commanddata1, void *commanddata2) +{ + if (controltype == FMOD_CHANNELCONTROL_CHANNEL && + callbacktype == FMOD_CHANNELCONTROL_CALLBACK_END) + { + FMOD::Channel* chan = reinterpret_cast(channel); + LLAudioChannelFMODSTUDIO* audio_channel = NULL; + chan->getUserData((void**)&audio_channel); + if(audio_channel) + { + audio_channel->onRelease(); + } + } + return FMOD_OK; +} + +void LLAudioChannelFMODSTUDIO::onRelease() +{ + llassert(mChannelp); + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Fmod signaled channel release for channel " << mChannelp << LL_ENDL; + gSoundCheck.removeChannel(mChannelp); + mChannelp = NULL; //Null out channel here so cleanup doesn't try to redundantly stop it. + cleanup(); +} + +bool LLAudioChannelFMODSTUDIO::updateBuffer() +{ + if (LLAudioChannel::updateBuffer()) + { + // Base class update returned true, which means the channel buffer was changed, and not is null. + + // Grab the FMOD sample associated with the buffer + FMOD::Sound *soundp = ((LLAudioBufferFMODSTUDIO*)mCurrentBufferp)->getSound(); + if (!soundp) + { + // This is bad, there should ALWAYS be a sound associated with a legit + // buffer. + LL_ERRS("AudioImpl") << "No FMOD sound!" << LL_ENDL; + return false; + } + + // Actually play the sound. Start it off paused so we can do all the necessary + // setup. + if(!mChannelp) + { + FMOD_RESULT result = getSystem()->playSound(soundp, NULL, true, &mChannelp); + Check_FMOD_Error(result, "FMOD::System::playSound"); + if(result == FMOD_OK) + { + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Created channel " << mChannelp << " for sound " << soundp << LL_ENDL; + + gSoundCheck.addNewChannelToSound(soundp,mChannelp); + mChannelp->setCallback(&channel_callback); + mChannelp->setUserData(this); + } + } + } + + // If we have a source for the channel, we need to update its gain. + if (mCurrentSourcep && mChannelp) + { + FMOD_RESULT result; + + gSoundCheck.assertActiveState(this); + result = mChannelp->setVolume(getSecondaryGain() * mCurrentSourcep->getGain()); + Check_FMOD_Error(result, "FMOD::Channel::setVolume"); + result = mChannelp->setMode(mCurrentSourcep->isLoop() ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF); + Check_FMOD_Error(result, "FMOD::Channel::setMode"); + } + + return true; +} + + +void LLAudioChannelFMODSTUDIO::update3DPosition() +{ + if (!mChannelp) + { + // We're not actually a live channel (i.e., we're not playing back anything) + return; + } + + LLAudioBufferFMODSTUDIO *bufferp = (LLAudioBufferFMODSTUDIO *)mCurrentBufferp; + if (!bufferp) + { + // We don't have a buffer associated with us (should really have been picked up + // by the above if. + return; + } + + gSoundCheck.assertActiveState(this); + + if (mCurrentSourcep->isAmbient()) + { + // Ambient sound, don't need to do any positional updates. + set3DMode(false); + } + else + { + // Localized sound. Update the position and velocity of the sound. + set3DMode(true); + + LLVector3 float_pos; + float_pos.setVec(mCurrentSourcep->getPositionGlobal()); + FMOD_RESULT result = mChannelp->set3DAttributes((FMOD_VECTOR*)float_pos.mV, (FMOD_VECTOR*)mCurrentSourcep->getVelocity().mV); + Check_FMOD_Error(result, "FMOD::Channel::set3DAttributes"); + } +} + + +void LLAudioChannelFMODSTUDIO::updateLoop() +{ + if (!mChannelp) + { + // May want to clear up the loop/sample counters. + return; + } + + gSoundCheck.assertActiveState(this); + + // + // Hack: We keep track of whether we looped or not by seeing when the + // sample position looks like it's going backwards. Not reliable; may + // yield false negatives. + // + U32 cur_pos; + Check_FMOD_Error(mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES),"FMOD::Channel::getPosition"); + + if (cur_pos < (U32)mLastSamplePos) + { + mLoopedThisFrame = true; + } + mLastSamplePos = cur_pos; +} + + +void LLAudioChannelFMODSTUDIO::cleanup() +{ + LLAudioChannel::cleanup(); + + if (!mChannelp) + { + llassert(mCurrentBufferp == NULL); + //LL_INFOS("AudioImpl") << "Aborting cleanup with no channel handle." << LL_ENDL; + return; + } + + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Stopping channel " << mChannelp << LL_ENDL; + + gSoundCheck.removeChannel(mChannelp); + mChannelp->setCallback(NULL); + Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop"); + + mChannelp = NULL; + mLastSamplePos = 0; +} + + +void LLAudioChannelFMODSTUDIO::play() +{ + if (!mChannelp) + { + LL_WARNS("AudioImpl") << "Playing without a channel handle, aborting" << LL_ENDL; + return; + } + + gSoundCheck.assertActiveState(this,true); + + bool paused=true; + Check_FMOD_Error(mChannelp->getPaused(&paused), "FMOD::Channel::getPaused"); + if(!paused) + { + Check_FMOD_Error(mChannelp->setPaused(true), "FMOD::Channel::setPaused"); + Check_FMOD_Error(mChannelp->setPosition(0,FMOD_TIMEUNIT_PCMBYTES), "FMOD::Channel::setPosition"); + } + Check_FMOD_Error(mChannelp->setPaused(false), "FMOD::Channel::setPaused"); + + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Playing channel " << mChannelp << LL_ENDL; + + getSource()->setPlayedOnce(true); + + if(LLAudioEngine_FMODSTUDIO::mChannelGroups[getSource()->getType()]) + Check_FMOD_Error(mChannelp->setChannelGroup(LLAudioEngine_FMODSTUDIO::mChannelGroups[getSource()->getType()]),"FMOD::Channel::setChannelGroup"); +} + + +void LLAudioChannelFMODSTUDIO::playSynced(LLAudioChannel *channelp) +{ + LLAudioChannelFMODSTUDIO *fmod_channelp = (LLAudioChannelFMODSTUDIO*)channelp; + if (!(fmod_channelp->mChannelp && mChannelp)) + { + // Don't have channels allocated to both the master and the slave + return; + } + + gSoundCheck.assertActiveState(this,true); + + U32 cur_pos; + if(Check_FMOD_Error(fmod_channelp->mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES), "Unable to retrieve current position")) + return; + + cur_pos %= mCurrentBufferp->getLength(); + + // Try to match the position of our sync master + Check_FMOD_Error(mChannelp->setPosition(cur_pos,FMOD_TIMEUNIT_PCMBYTES),"Unable to set current position"); + + // Start us playing + play(); +} + + +bool LLAudioChannelFMODSTUDIO::isPlaying() +{ + if (!mChannelp) + { + return false; + } + + gSoundCheck.assertActiveState(this); + + bool paused, playing; + Check_FMOD_Error(mChannelp->getPaused(&paused),"FMOD::Channel::getPaused"); + Check_FMOD_Error(mChannelp->isPlaying(&playing),"FMOD::Channel::isPlaying"); + return !paused && playing; +} + + +// +// LLAudioChannelFMODSTUDIO implementation +// + + +LLAudioBufferFMODSTUDIO::LLAudioBufferFMODSTUDIO(FMOD::System *system) : LLAudioBuffer(), mSystemp(system), mSoundp(NULL) +{ +} + + +LLAudioBufferFMODSTUDIO::~LLAudioBufferFMODSTUDIO() +{ + if(mSoundp) + { + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Release sound " << mSoundp << LL_ENDL; + + gSoundCheck.removeSound(mSoundp); + Check_FMOD_Error(mSoundp->release(),"FMOD::Sound::Release"); + mSoundp = NULL; + } +} + + +bool LLAudioBufferFMODSTUDIO::loadWAV(const std::string& filename) +{ + // Try to open a wav file from disk. This will eventually go away, as we don't + // really want to block doing this. + if (filename.empty()) + { + // invalid filename, abort. + return false; + } + + if (!LLAPRFile::isExist(filename, LL_APR_RPB)) + { + // File not found, abort. + return false; + } + + if (mSoundp) + { + gSoundCheck.removeSound(mSoundp); + // If there's already something loaded in this buffer, clean it up. + Check_FMOD_Error(mSoundp->release(),"FMOD::Sound::release"); + mSoundp = NULL; + } + + FMOD_MODE base_mode = FMOD_LOOP_NORMAL; + FMOD_CREATESOUNDEXINFO exinfo; + memset(&exinfo,0,sizeof(exinfo)); + exinfo.cbsize = sizeof(exinfo); + exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_WAV; //Hint to speed up loading. + // Load up the wav file into an fmod sample +#if LL_WINDOWS + FMOD_RESULT result = getSystem()->createSound((const char*)utf8str_to_utf16str(filename).c_str(), base_mode | FMOD_UNICODE, &exinfo, &mSoundp); +#else + FMOD_RESULT result = getSystem()->createSound(filename.c_str(), base_mode, &exinfo, &mSoundp); +#endif + + if (result != FMOD_OK) + { + // We failed to load the file for some reason. + LL_WARNS("AudioImpl") << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << LL_ENDL; + + // + // If we EVER want to load wav files provided by end users, we need + // to rethink this! + // + // file is probably corrupt - remove it. + LLFile::remove(filename); + return false; + } + + gSoundCheck.addNewSound(mSoundp); + + // Everything went well, return true + return true; +} + + +U32 LLAudioBufferFMODSTUDIO::getLength() +{ + if (!mSoundp) + { + return 0; + } + + gSoundCheck.assertActiveState(this); + U32 length; + Check_FMOD_Error(mSoundp->getLength(&length, FMOD_TIMEUNIT_PCMBYTES),"FMOD::Sound::getLength"); + return length; +} + + +void LLAudioChannelFMODSTUDIO::set3DMode(bool use3d) +{ + gSoundCheck.assertActiveState(this); + + FMOD_MODE current_mode; + if(Check_FMOD_Error(mChannelp->getMode(¤t_mode),"FMOD::Channel::getMode")) + return; + FMOD_MODE new_mode = current_mode; + new_mode &= ~(use3d ? FMOD_2D : FMOD_3D); + new_mode |= use3d ? FMOD_3D : FMOD_2D; + + if(current_mode != new_mode) + { + Check_FMOD_Error(mChannelp->setMode(new_mode),"FMOD::Channel::setMode"); + } +} + + +FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *originalbuffer, float *newbuffer, unsigned int length, int inchannels, int *outchannels) +{ + // originalbuffer = fmod's original mixbuffer. + // newbuffer = the buffer passed from the previous DSP unit. + // length = length in samples at this mix time. + // userdata = user parameter passed through in FSOUND_DSP_Create. + + LLWindGen *windgen; + FMOD::DSP *thisdsp = (FMOD::DSP *)dsp_state->instance; + + thisdsp->getUserData((void **)&windgen); + S32 channels, configwidth, configheight; + thisdsp->getInfo(0, 0, &channels, &configwidth, &configheight); + + windgen->windGenerate((LLAudioEngine_FMODSTUDIO::MIXBUFFERFORMAT *)newbuffer, length); + + return FMOD_OK; +} diff --git a/indra/llaudio/llaudioengine_fmodstudio.h b/indra/llaudio/llaudioengine_fmodstudio.h new file mode 100644 index 000000000..730a702a1 --- /dev/null +++ b/indra/llaudio/llaudioengine_fmodstudio.h @@ -0,0 +1,139 @@ +/** + * @file audioengine_FMODSTUDIO.h + * @brief Definition of LLAudioEngine class abstracting the audio + * support as a FMOD Studio 3D implementation + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_AUDIOENGINE_FMODSTUDIO_H +#define LL_AUDIOENGINE_FMODSTUDIO_H + +#include "llaudioengine.h" +#include "lllistener_fmodstudio.h" +#include "llwindgen.h" + +//Stubs +class LLAudioStreamManagerFMODSTUDIO; +namespace FMOD +{ + class System; + class Channel; + class ChannelGroup; + class Sound; + class DSP; +} + +//Interfaces +class LLAudioEngine_FMODSTUDIO : public LLAudioEngine +{ +public: + LLAudioEngine_FMODSTUDIO(bool enable_profiler, bool verbose_debugging); + virtual ~LLAudioEngine_FMODSTUDIO(); + + // initialization/startup/shutdown + virtual bool init(const S32 num_channels, void *user_data); + virtual std::string getDriverName(bool verbose); + virtual void allocateListener(); + + virtual void shutdown(); + + /*virtual*/ bool initWind(); + /*virtual*/ void cleanupWind(); + + /*virtual*/void updateWind(LLVector3 direction, F32 camera_height_above_water); + + typedef F32 MIXBUFFERFORMAT; + + FMOD::System *getSystem() const {return mSystem;} +protected: + /*virtual*/ LLAudioBuffer *createBuffer(); // Get a free buffer, or flush an existing one if you have to. + /*virtual*/ LLAudioChannel *createChannel(); // Create a new audio channel. + + /*virtual*/ void setInternalGain(F32 gain); + + bool mInited; + + LLWindGen *mWindGen; + + FMOD::DSP *mWindDSP; + FMOD::System *mSystem; + bool mEnableProfiler; + +public: + static FMOD::ChannelGroup *mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT]; +}; + + +class LLAudioChannelFMODSTUDIO : public LLAudioChannel +{ +public: + LLAudioChannelFMODSTUDIO(FMOD::System *audioengine); + virtual ~LLAudioChannelFMODSTUDIO(); + void onRelease(); +protected: + /*virtual*/ void play(); + /*virtual*/ void playSynced(LLAudioChannel *channelp); + /*virtual*/ void cleanup(); + /*virtual*/ bool isPlaying(); + + /*virtual*/ bool updateBuffer(); + /*virtual*/ void update3DPosition(); + /*virtual*/ void updateLoop(); + + void set3DMode(bool use3d); +protected: + FMOD::System *getSystem() const {return mSystemp;} + FMOD::System *mSystemp; + FMOD::Channel *mChannelp; + S32 mLastSamplePos; + + friend class CFMODSoundChecks; +}; + + +class LLAudioBufferFMODSTUDIO : public LLAudioBuffer +{ +public: + LLAudioBufferFMODSTUDIO(FMOD::System *audioengine); + virtual ~LLAudioBufferFMODSTUDIO(); + + /*virtual*/ bool loadWAV(const std::string& filename); + /*virtual*/ U32 getLength(); + friend class LLAudioChannelFMODSTUDIO; +protected: + FMOD::System *getSystem() const {return mSystemp;} + FMOD::System *mSystemp; + FMOD::Sound *getSound() const{ return mSoundp; } + FMOD::Sound *mSoundp; + + friend class CFMODSoundChecks; +}; + + +#endif // LL_AUDIOENGINE_FMODSTUDIO_H diff --git a/indra/llaudio/lllistener_fmod.cpp b/indra/llaudio/lllistener_fmod.cpp deleted file mode 100644 index 0138f4345..000000000 --- a/indra/llaudio/lllistener_fmod.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/** - * @file listener_fmod.cpp - * @brief implementation of LISTENER class abstracting the audio - * support as a FMOD 3D implementation (windows only) - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llaudioengine.h" -#include "lllistener_fmod.h" -#include "fmod.h" - -//----------------------------------------------------------------------- -// constructor -//----------------------------------------------------------------------- -LLListener_FMOD::LLListener_FMOD() -{ - init(); -} - -//----------------------------------------------------------------------- -LLListener_FMOD::~LLListener_FMOD() -{ -} - -//----------------------------------------------------------------------- -void LLListener_FMOD::init(void) -{ - // do inherited - LLListener::init(); - mDopplerFactor = 1.0f; - mRolloffFactor = 1.0f; -} - -//----------------------------------------------------------------------- -void LLListener_FMOD::translate(LLVector3 offset) -{ - LLListener::translate(offset); - - FSOUND_3D_Listener_SetAttributes(mPosition.mV, NULL, mListenAt.mV[0],mListenAt.mV[1],mListenAt.mV[2], mListenUp.mV[0],mListenUp.mV[1],mListenUp.mV[2]); -} - -//----------------------------------------------------------------------- -void LLListener_FMOD::setPosition(LLVector3 pos) -{ - LLListener::setPosition(pos); - - FSOUND_3D_Listener_SetAttributes(pos.mV, NULL, mListenAt.mV[0],mListenAt.mV[1],mListenAt.mV[2], mListenUp.mV[0],mListenUp.mV[1],mListenUp.mV[2]); -} - -//----------------------------------------------------------------------- -void LLListener_FMOD::setVelocity(LLVector3 vel) -{ - LLListener::setVelocity(vel); - - FSOUND_3D_Listener_SetAttributes(NULL, vel.mV, mListenAt.mV[0],mListenAt.mV[1],mListenAt.mV[2], mListenUp.mV[0],mListenUp.mV[1],mListenUp.mV[2]); -} - -//----------------------------------------------------------------------- -void LLListener_FMOD::orient(LLVector3 up, LLVector3 at) -{ - LLListener::orient(up, at); - - // Welcome to the transition between right and left - // (coordinate systems, that is) - // Leaving the at vector alone results in a L/R reversal - // since DX is left-handed and we (LL, OpenGL, OpenAL) are right-handed - at = -at; - - FSOUND_3D_Listener_SetAttributes(NULL, NULL, at.mV[0],at.mV[1],at.mV[2], up.mV[0],up.mV[1],up.mV[2]); -} - -//----------------------------------------------------------------------- -void LLListener_FMOD::commitDeferredChanges() -{ - FSOUND_Update(); -} - - -void LLListener_FMOD::setRolloffFactor(F32 factor) -{ - mRolloffFactor = factor; - FSOUND_3D_SetRolloffFactor(factor); -} - - -F32 LLListener_FMOD::getRolloffFactor() -{ - return mRolloffFactor; -} - - -void LLListener_FMOD::setDopplerFactor(F32 factor) -{ - mDopplerFactor = factor; - FSOUND_3D_SetDopplerFactor(factor); -} - - -F32 LLListener_FMOD::getDopplerFactor() -{ - return mDopplerFactor; -} - - diff --git a/indra/llaudio/lllistener_fmod.h b/indra/llaudio/lllistener_fmod.h deleted file mode 100644 index 818da05d5..000000000 --- a/indra/llaudio/lllistener_fmod.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @file listener_fmod.h - * @brief Description of LISTENER class abstracting the audio support - * as an FMOD 3D implementation (windows and Linux) - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LISTENER_FMOD_H -#define LL_LISTENER_FMOD_H - -#include "lllistener.h" - -class LLListener_FMOD : public LLListener -{ - public: - LLListener_FMOD(); - virtual ~LLListener_FMOD(); - virtual void init(); - - virtual void translate(LLVector3 offset); - virtual void setPosition(LLVector3 pos); - virtual void setVelocity(LLVector3 vel); - virtual void orient(LLVector3 up, LLVector3 at); - virtual void commitDeferredChanges(); - - virtual void setDopplerFactor(F32 factor); - virtual F32 getDopplerFactor(); - virtual void setRolloffFactor(F32 factor); - virtual F32 getRolloffFactor(); - - protected: - F32 mDopplerFactor; - F32 mRolloffFactor; -}; - -#endif - - diff --git a/indra/llaudio/lllistener_fmodstudio.cpp b/indra/llaudio/lllistener_fmodstudio.cpp new file mode 100644 index 000000000..f6ee7908b --- /dev/null +++ b/indra/llaudio/lllistener_fmodstudio.cpp @@ -0,0 +1,141 @@ +/** + * @file listener_fmodstudio.cpp + * @brief implementation of LISTENER class abstracting the audio + * support as a FMOD 3D implementation (windows only) + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llaudioengine.h" +#include "lllistener_fmodstudio.h" +#include "fmod.hpp" + +//----------------------------------------------------------------------- +// constructor +//----------------------------------------------------------------------- +LLListener_FMODSTUDIO::LLListener_FMODSTUDIO(FMOD::System *system) +{ + mSystem = system; + init(); +} + +//----------------------------------------------------------------------- +LLListener_FMODSTUDIO::~LLListener_FMODSTUDIO() +{ +} + +//----------------------------------------------------------------------- +void LLListener_FMODSTUDIO::init(void) +{ + // do inherited + LLListener::init(); + mDopplerFactor = 1.0f; + mRolloffFactor = 1.0f; +} + +//----------------------------------------------------------------------- +void LLListener_FMODSTUDIO::translate(LLVector3 offset) +{ + LLListener::translate(offset); + + mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV); +} + +//----------------------------------------------------------------------- +void LLListener_FMODSTUDIO::setPosition(LLVector3 pos) +{ + LLListener::setPosition(pos); + + mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV); +} + +//----------------------------------------------------------------------- +void LLListener_FMODSTUDIO::setVelocity(LLVector3 vel) +{ + LLListener::setVelocity(vel); + + mSystem->set3DListenerAttributes(0, NULL, (FMOD_VECTOR*)mVelocity.mV, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV); +} + +//----------------------------------------------------------------------- +void LLListener_FMODSTUDIO::orient(LLVector3 up, LLVector3 at) +{ + LLListener::orient(up, at); + + // Welcome to the transition between right and left + // (coordinate systems, that is) + // Leaving the at vector alone results in a L/R reversal + // since DX is left-handed and we (LL, OpenGL, OpenAL) are right-handed + at = -at; + + mSystem->set3DListenerAttributes(0, NULL, NULL, (FMOD_VECTOR*)at.mV, (FMOD_VECTOR*)up.mV); +} + +//----------------------------------------------------------------------- +void LLListener_FMODSTUDIO::commitDeferredChanges() +{ + mSystem->update(); +} + + +void LLListener_FMODSTUDIO::setRolloffFactor(F32 factor) +{ + //An internal FMODEx optimization skips 3D updates if there have not been changes to the 3D sound environment. + //Sadly, a change in rolloff is not accounted for, thus we must touch the listener properties as well. + //In short: Changing the position ticks a dirtyflag inside fmodstudio, which makes it not skip 3D processing next update call. + if(mRolloffFactor != factor) + { + LLVector3 pos = mVelocity - LLVector3(0.f,0.f,.1f); + mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)pos.mV, NULL, NULL, NULL); + mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mVelocity.mV, NULL, NULL, NULL); + } + mRolloffFactor = factor; + mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor); +} + + +F32 LLListener_FMODSTUDIO::getRolloffFactor() +{ + return mRolloffFactor; +} + + +void LLListener_FMODSTUDIO::setDopplerFactor(F32 factor) +{ + mDopplerFactor = factor; + mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor); +} + + +F32 LLListener_FMODSTUDIO::getDopplerFactor() +{ + return mDopplerFactor; +} + + diff --git a/indra/llaudio/lllistener_fmodstudio.h b/indra/llaudio/lllistener_fmodstudio.h new file mode 100644 index 000000000..516f4eae8 --- /dev/null +++ b/indra/llaudio/lllistener_fmodstudio.h @@ -0,0 +1,71 @@ +/** + * @file listener_fmodstudio.h + * @brief Description of LISTENER class abstracting the audio support + * as an FMOD Studio 3D implementation (windows and Linux) + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LISTENER_FMODSTUDIO_H +#define LL_LISTENER_FMODSTUDIO_H + +#include "lllistener.h" + +//Stubs +namespace FMOD +{ + class System; +} + +//Interfaces +class LLListener_FMODSTUDIO : public LLListener +{ + public: + LLListener_FMODSTUDIO(FMOD::System *system); + virtual ~LLListener_FMODSTUDIO(); + virtual void init(); + + virtual void translate(LLVector3 offset); + virtual void setPosition(LLVector3 pos); + virtual void setVelocity(LLVector3 vel); + virtual void orient(LLVector3 up, LLVector3 at); + virtual void commitDeferredChanges(); + + virtual void setDopplerFactor(F32 factor); + virtual F32 getDopplerFactor(); + virtual void setRolloffFactor(F32 factor); + virtual F32 getRolloffFactor(); + protected: + FMOD::System *mSystem; + F32 mDopplerFactor; + F32 mRolloffFactor; +}; + +#endif + + diff --git a/indra/llaudio/llstreamingaudio_fmod.cpp b/indra/llaudio/llstreamingaudio_fmod.cpp deleted file mode 100644 index 4036b8df6..000000000 --- a/indra/llaudio/llstreamingaudio_fmod.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/** - * @file streamingaudio_fmod.cpp - * @brief LLStreamingAudio_FMOD implementation - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llmath.h" - -#include "fmod.h" -#include "fmod_errors.h" - -#include "llstreamingaudio_fmod.h" - - -class LLAudioStreamManagerFMOD -{ -public: - LLAudioStreamManagerFMOD(const std::string& url); - int startStream(); - bool stopStream(); // Returns true if the stream was successfully stopped. - bool ready(); - - const std::string& getURL() { return mInternetStreamURL; } - - int getOpenState(); - - FSOUND_STREAM* getStream() { return mInternetStream; } -protected: - FSOUND_STREAM* mInternetStream; - bool mReady; - - std::string mInternetStreamURL; -}; - - - -//--------------------------------------------------------------------------- -// Internet Streaming -//--------------------------------------------------------------------------- -LLStreamingAudio_FMOD::LLStreamingAudio_FMOD() : - mCurrentInternetStreamp(NULL), - mFMODInternetStreamChannel(-1), - mGain(1.0f), - mMetaData(NULL) -{ - // Number of milliseconds of audio to buffer for the audio card. - // Must be larger than the usual Second Life frame stutter time. - FSOUND_Stream_SetBufferSize(200); - - // Here's where we set the size of the network buffer and some buffering - // parameters. In this case we want a network buffer of 16k, we want it - // to prebuffer 40% of that when we first connect, and we want it - // to rebuffer 80% of that whenever we encounter a buffer underrun. - - // Leave the net buffer properties at the default. - //FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80); -} - - -LLStreamingAudio_FMOD::~LLStreamingAudio_FMOD() -{ - // nothing interesting/safe to do. -} - -signed char F_CALLBACKAPI MetaDataCallback(char *name, char *value, void *userdata) -{ - std::string szName(name); - if(szName == "TITLE" || szName=="TIT2" || szName=="Title") - (*(LLSD*)userdata)["TITLE"] = value; - if(szName == "ARTIST" || szName=="TPE1" || szName =="WM/AlbumTitle") - (*(LLSD*)userdata)["ARTIST"] = value; - else - (*(LLSD*)userdata)[std::string(name)] = value; - return true; -} - -void LLStreamingAudio_FMOD::start(const std::string& url) -{ - //if (!mInited) - //{ - // llwarns << "startInternetStream before audio initialized" << llendl; - // return; - //} - - // "stop" stream but don't clear url, etc. in case url == mInternetStreamURL - stop(); - - if (!url.empty()) - { - llinfos << "Starting internet stream: " << url << llendl; - mCurrentInternetStreamp = new LLAudioStreamManagerFMOD(url); - mURL = url; - if(mCurrentInternetStreamp->getStream()) - { - mMetaData = new LLSD; - } - } - else - { - llinfos << "Set internet stream to null" << llendl; - mURL.clear(); - } -} - - -void LLStreamingAudio_FMOD::update() -{ - // Kill dead internet streams, if possible - std::list::iterator iter; - for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();) - { - LLAudioStreamManagerFMOD *streamp = *iter; - if (streamp->stopStream()) - { - llinfos << "Closed dead stream" << llendl; - delete streamp; - mDeadStreams.erase(iter++); - } - else - { - iter++; - } - } - - // Don't do anything if there are no streams playing - if (!mCurrentInternetStreamp) - { - return; - } - - int open_state = mCurrentInternetStreamp->getOpenState(); - - if (!open_state) - { - // Stream is live - - // start the stream if it's ready - if (mFMODInternetStreamChannel < 0) - { - mFMODInternetStreamChannel = mCurrentInternetStreamp->startStream(); - - if (mFMODInternetStreamChannel != -1) - { - // Reset volume to previously set volume - setGain(getGain()); - FSOUND_SetPaused(mFMODInternetStreamChannel, false); - if(mCurrentInternetStreamp->getStream() && mMetaData) - { - FSOUND_Stream_Net_SetMetadataCallback(mCurrentInternetStreamp->getStream(),&MetaDataCallback, mMetaData); - } - } - } - } - - switch(open_state) - { - default: - case 0: - // success - break; - case -1: - // stream handle is invalid - llwarns << "InternetStream - invalid handle" << llendl; - stop(); - return; - case -2: - // opening - break; - case -3: - // failed to open, file not found, perhaps - llwarns << "InternetStream - failed to open" << llendl; - stop(); - return; - case -4: - // connecting - break; - case -5: - // buffering - break; - } - -} - -void LLStreamingAudio_FMOD::stop() -{ - if(mMetaData) - { - if(mCurrentInternetStreamp && mCurrentInternetStreamp->getStream()) - FSOUND_Stream_Net_SetMetadataCallback(mCurrentInternetStreamp->getStream(),NULL,NULL); - delete mMetaData; - mMetaData = NULL; - } - if (mFMODInternetStreamChannel != -1) - { - FSOUND_SetPaused(mFMODInternetStreamChannel, true); - FSOUND_SetPriority(mFMODInternetStreamChannel, 0); - mFMODInternetStreamChannel = -1; - } - - if (mCurrentInternetStreamp) - { - llinfos << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << llendl; - if (mCurrentInternetStreamp->stopStream()) - { - delete mCurrentInternetStreamp; - } - else - { - llwarns << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << llendl; - mDeadStreams.push_back(mCurrentInternetStreamp); - } - mCurrentInternetStreamp = NULL; - //mURL.clear(); - } -} - -void LLStreamingAudio_FMOD::pause(int pauseopt) -{ - if (pauseopt < 0) - { - pauseopt = mCurrentInternetStreamp ? 1 : 0; - } - - if (pauseopt) - { - if (mCurrentInternetStreamp) - { - stop(); - } - } - else - { - start(getURL()); - } -} - - -// A stream is "playing" if it has been requested to start. That -// doesn't necessarily mean audio is coming out of the speakers. -int LLStreamingAudio_FMOD::isPlaying() -{ - if (mCurrentInternetStreamp) - { - return 1; // Active and playing - } - else if (!mURL.empty()) - { - return 2; // "Paused" - } - else - { - return 0; - } -} - - -F32 LLStreamingAudio_FMOD::getGain() -{ - return mGain; -} - - -std::string LLStreamingAudio_FMOD::getURL() -{ - return mURL; -} - - -void LLStreamingAudio_FMOD::setGain(F32 vol) -{ - mGain = vol; - - if (mFMODInternetStreamChannel != -1) - { - vol = llclamp(vol * vol, 0.f, 1.f); - int vol_int = llround(vol * 255.f); - FSOUND_SetVolumeAbsolute(mFMODInternetStreamChannel, vol_int); - } -} - - -/////////////////////////////////////////////////////// -// manager of possibly-multiple internet audio streams - -LLAudioStreamManagerFMOD::LLAudioStreamManagerFMOD(const std::string& url) : - mInternetStream(NULL), - mReady(false) -{ - mInternetStreamURL = url; - mInternetStream = FSOUND_Stream_Open(url.c_str(), FSOUND_NORMAL | FSOUND_NONBLOCKING, 0, 0); - if (!mInternetStream) - { - llwarns << "Couldn't open fmod stream, error " - << FMOD_ErrorString(FSOUND_GetError()) - << llendl; - mReady = false; - return; - } - - mReady = true; -} - -int LLAudioStreamManagerFMOD::startStream() -{ - // We need a live and opened stream before we try and play it. - if (!mInternetStream || getOpenState()) - { - llwarns << "No internet stream to start playing!" << llendl; - return -1; - } - - // Make sure the stream is set to 2D mode. - FSOUND_Stream_SetMode(mInternetStream, FSOUND_2D); - - return FSOUND_Stream_PlayEx(FSOUND_FREE, mInternetStream, NULL, true); -} - -bool LLAudioStreamManagerFMOD::stopStream() -{ - if (mInternetStream) - { - int read_percent = 0; - int status = 0; - int bitrate = 0; - unsigned int flags = 0x0; - FSOUND_Stream_Net_GetStatus(mInternetStream, &status, &read_percent, &bitrate, &flags); - - bool close = true; - switch (status) - { - case FSOUND_STREAM_NET_CONNECTING: - close = false; - break; - case FSOUND_STREAM_NET_NOTCONNECTED: - case FSOUND_STREAM_NET_BUFFERING: - case FSOUND_STREAM_NET_READY: - case FSOUND_STREAM_NET_ERROR: - default: - close = true; - } - - if (close) - { - FSOUND_Stream_Close(mInternetStream); - mInternetStream = NULL; - return true; - } - else - { - return false; - } - } - else - { - return true; - } -} - -int LLAudioStreamManagerFMOD::getOpenState() -{ - int open_state = FSOUND_Stream_GetOpenState(mInternetStream); - return open_state; -} diff --git a/indra/llaudio/llstreamingaudio_fmod.h b/indra/llaudio/llstreamingaudio_fmod.h deleted file mode 100644 index 4bb65c54d..000000000 --- a/indra/llaudio/llstreamingaudio_fmod.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @file streamingaudio_fmod.h - * @author Tofu Linden - * @brief Definition of LLStreamingAudio_FMOD implementation - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_STREAMINGAUDIO_FMOD_H -#define LL_STREAMINGAUDIO_FMOD_H - -#include "stdtypes.h" // from llcommon - -#include "llstreamingaudio.h" - -class LLAudioStreamManagerFMOD; - -class LLStreamingAudio_FMOD : public LLStreamingAudioInterface -{ - public: - LLStreamingAudio_FMOD(); - /*virtual*/ ~LLStreamingAudio_FMOD(); - - /*virtual*/ void start(const std::string& url); - /*virtual*/ void stop(); - /*virtual*/ void pause(int pause); - /*virtual*/ void update(); - /*virtual*/ int isPlaying(); - /*virtual*/ void setGain(F32 vol); - /*virtual*/ F32 getGain(); - /*virtual*/ std::string getURL(); - - /*virtual*/ bool supportsMetaData(){return true;} - /*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not playing. - /*virtual*/ bool supportsWaveData(){return false;} - /*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1){return false;} -private: - LLAudioStreamManagerFMOD *mCurrentInternetStreamp; - int mFMODInternetStreamChannel; - std::list mDeadStreams; - - std::string mURL; - F32 mGain; - - LLSD *mMetaData; -}; - - -#endif // LL_STREAMINGAUDIO_FMOD_H diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.cpp b/indra/llaudio/llstreamingaudio_fmodstudio.cpp new file mode 100644 index 000000000..a2ffe2108 --- /dev/null +++ b/indra/llaudio/llstreamingaudio_fmodstudio.cpp @@ -0,0 +1,505 @@ +/** + * @file streamingaudio_fmodstudio.cpp + * @brief LLStreamingAudio_FMODSTUDIO implementation + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llmath.h" + +#include "fmod.hpp" +#include "fmod_errors.h" + +#include "llstreamingaudio_fmodstudio.h" + + +class LLAudioStreamManagerFMODSTUDIO +{ +public: + LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, const std::string& url); + FMOD::Channel* startStream(); + bool stopStream(); // Returns true if the stream was successfully stopped. + bool ready(); + + const std::string& getURL() { return mInternetStreamURL; } + + FMOD_OPENSTATE getOpenState(unsigned int* percentbuffered=NULL, bool* starving=NULL, bool* diskbusy=NULL); +protected: + FMOD::System* mSystem; + FMOD::Channel* mStreamChannel; + FMOD::Sound* mInternetStream; + bool mReady; + + std::string mInternetStreamURL; +}; + + + +//--------------------------------------------------------------------------- +// Internet Streaming +//--------------------------------------------------------------------------- +LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) : + mSystem(system), + mCurrentInternetStreamp(NULL), + mFMODInternetStreamChannelp(NULL), + mGain(1.0f), + mMetaData(NULL) +{ + // Number of milliseconds of audio to buffer for the audio card. + // Must be larger than the usual Second Life frame stutter time. + const U32 buffer_seconds = 10; //sec + const U32 estimated_bitrate = 128; //kbit/sec + mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES); + + // Here's where we set the size of the network buffer and some buffering + // parameters. In this case we want a network buffer of 16k, we want it + // to prebuffer 40% of that when we first connect, and we want it + // to rebuffer 80% of that whenever we encounter a buffer underrun. + + // Leave the net buffer properties at the default. + //FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80); +} + + +LLStreamingAudio_FMODSTUDIO::~LLStreamingAudio_FMODSTUDIO() +{ + // nothing interesting/safe to do. +} + + +void LLStreamingAudio_FMODSTUDIO::start(const std::string& url) +{ + //if (!mInited) + //{ + // llwarns << "startInternetStream before audio initialized" << llendl; + // return; + //} + + // "stop" stream but don't clear url, etc. in case url == mInternetStreamURL + stop(); + + if (!url.empty()) + { + if(mDeadStreams.empty()) + { + llinfos << "Starting internet stream: " << url << llendl; + mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem,url); + mURL = url; + mMetaData = new LLSD; + } + else + { + llinfos << "Deferring stream load until buffer release: " << url << llendl; + mPendingURL = url; + } + } + else + { + llinfos << "Set internet stream to null" << llendl; + mURL.clear(); + } +} + + +void LLStreamingAudio_FMODSTUDIO::update() +{ + // Kill dead internet streams, if possible + std::list::iterator iter; + for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();) + { + LLAudioStreamManagerFMODSTUDIO *streamp = *iter; + if (streamp->stopStream()) + { + llinfos << "Closed dead stream" << llendl; + delete streamp; + mDeadStreams.erase(iter++); + } + else + { + iter++; + } + } + + if(!mDeadStreams.empty()) + { + llassert_always(mCurrentInternetStreamp == NULL); + return; + } + + if(!mPendingURL.empty()) + { + llassert_always(mCurrentInternetStreamp == NULL); + llinfos << "Starting internet stream: " << mPendingURL << llendl; + mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem,mPendingURL); + mURL = mPendingURL; + mMetaData = new LLSD; + mPendingURL.clear(); + } + + // Don't do anything if there are no streams playing + if (!mCurrentInternetStreamp) + { + return; + } + + unsigned int progress; + bool starving; + bool diskbusy; + FMOD_OPENSTATE open_state = mCurrentInternetStreamp->getOpenState(&progress, &starving, &diskbusy); + + if (open_state == FMOD_OPENSTATE_READY) + { + // Stream is live + + // start the stream if it's ready + if (!mFMODInternetStreamChannelp && + (mFMODInternetStreamChannelp = mCurrentInternetStreamp->startStream())) + { + // Reset volume to previously set volume + setGain(getGain()); + mFMODInternetStreamChannelp->setPaused(false); + } + } + else if(open_state == FMOD_OPENSTATE_ERROR) + { + stop(); + return; + } + + if(mFMODInternetStreamChannelp) + { + if(!mMetaData) + mMetaData = new LLSD; + + FMOD::Sound *sound = NULL; + + if(mFMODInternetStreamChannelp->getCurrentSound(&sound) == FMOD_OK && sound) + { + FMOD_TAG tag; + S32 tagcount, dirtytagcount; + if(sound->getNumTags(&tagcount, &dirtytagcount) == FMOD_OK && dirtytagcount) + { + mMetaData->clear(); + + for(S32 i = 0; i < tagcount; ++i) + { + if(sound->getTag(NULL, i, &tag)!=FMOD_OK) + continue; + std::string name = tag.name; + switch(tag.type) //Crappy tag translate table. + { + case(FMOD_TAGTYPE_ID3V2): + if(name == "TIT2") name = "TITLE"; + else if(name == "TPE1") name = "ARTIST"; + break; + case(FMOD_TAGTYPE_ASF): + if(name == "Title") name = "TITLE"; + else if(name == "WM/AlbumArtist") name = "ARTIST"; + break; + case(FMOD_TAGTYPE_FMOD): + if (!strcmp(tag.name, "Sample Rate Change")) + { + llinfos << "Stream forced changing sample rate to " << *((float *)tag.data) << llendl; + mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data)); + } + continue; + default: + break; + } + switch(tag.datatype) + { + case(FMOD_TAGDATATYPE_INT): + (*mMetaData)[name]=*(LLSD::Integer*)(tag.data); + llinfos << tag.name << ": " << *(int*)(tag.data) << llendl; + break; + case(FMOD_TAGDATATYPE_FLOAT): + (*mMetaData)[name]=*(LLSD::Float*)(tag.data); + llinfos << tag.name << ": " << *(float*)(tag.data) << llendl; + break; + case(FMOD_TAGDATATYPE_STRING): + { + std::string out = rawstr_to_utf8(std::string((char*)tag.data,tag.datalen)); + (*mMetaData)[name]=out; + llinfos << tag.name << ": " << out << llendl; + } + break; + case(FMOD_TAGDATATYPE_STRING_UTF16): + { + std::string out((char*)tag.data,tag.datalen); + (*mMetaData)[std::string(tag.name)]=out; + llinfos << tag.name << ": " << out << llendl; + } + break; + case(FMOD_TAGDATATYPE_STRING_UTF16BE): + { + std::string out((char*)tag.data,tag.datalen); + U16* buf = (U16*)out.c_str(); + for(U32 j = 0; j < out.size()/2; ++j) + (((buf[j] & 0xff)<<8) | ((buf[j] & 0xff00)>>8)); + (*mMetaData)[std::string(tag.name)]=out; + llinfos << tag.name << ": " << out << llendl; + } + default: + break; + } + } + } + if(starving) + { + bool paused = false; + mFMODInternetStreamChannelp->getPaused(&paused); + if(!paused) + { + llinfos << "Stream starvation detected! Pausing stream until buffer nearly full." << llendl; + llinfos << " (diskbusy="<setPaused(true); + } + } + else if(progress > 80) + { + mFMODInternetStreamChannelp->setPaused(false); + } + } + } +} + +void LLStreamingAudio_FMODSTUDIO::stop() +{ + mPendingURL.clear(); + + if(mMetaData) + { + delete mMetaData; + mMetaData = NULL; + } + if (mFMODInternetStreamChannelp) + { + mFMODInternetStreamChannelp->setPaused(true); + mFMODInternetStreamChannelp->setPriority(0); + mFMODInternetStreamChannelp = NULL; + } + + if (mCurrentInternetStreamp) + { + llinfos << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << llendl; + if (mCurrentInternetStreamp->stopStream()) + { + delete mCurrentInternetStreamp; + } + else + { + llwarns << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << llendl; + mDeadStreams.push_back(mCurrentInternetStreamp); + } + mCurrentInternetStreamp = NULL; + //mURL.clear(); + } +} + +void LLStreamingAudio_FMODSTUDIO::pause(int pauseopt) +{ + if (pauseopt < 0) + { + pauseopt = mCurrentInternetStreamp ? 1 : 0; + } + + if (pauseopt) + { + if (mCurrentInternetStreamp) + { + stop(); + } + } + else + { + start(getURL()); + } +} + + +// A stream is "playing" if it has been requested to start. That +// doesn't necessarily mean audio is coming out of the speakers. +int LLStreamingAudio_FMODSTUDIO::isPlaying() +{ + if (mCurrentInternetStreamp) + { + return 1; // Active and playing + } + else if (!mURL.empty() || !mPendingURL.empty()) + { + return 2; // "Paused" + } + else + { + return 0; + } +} + + +F32 LLStreamingAudio_FMODSTUDIO::getGain() +{ + return mGain; +} + + +std::string LLStreamingAudio_FMODSTUDIO::getURL() +{ + return mURL; +} + + +void LLStreamingAudio_FMODSTUDIO::setGain(F32 vol) +{ + mGain = vol; + + if (mFMODInternetStreamChannelp) + { + vol = llclamp(vol * vol, 0.f, 1.f); //should vol be squared here? + + mFMODInternetStreamChannelp->setVolume(vol); + } +} + +/*virtual*/ bool LLStreamingAudio_FMODSTUDIO::getWaveData(float* arr, S32 count, S32 stride/*=1*/) +{ + //if(!mFMODInternetStreamChannelp || !mCurrentInternetStreamp) + // return false; + + //bool muted=false; + //mFMODInternetStreamChannelp->getMute(&muted); + //if(muted) + // return false; + + //static std::vector local_array(count); //Have to have an extra buffer to mix channels. Bleh. + //if(count > (S32)local_array.size()) //Expand the array if needed. Try to minimize allocation calls, so don't ever shrink. + // local_array.resize(count); + + //if( mFMODInternetStreamChannelp->getWaveData(&local_array[0],count,0) == FMOD_OK && + // mFMODInternetStreamChannelp->getWaveData(&arr[0],count,1) == FMOD_OK ) + //{ + // for(S32 i = count-1;i>=0;i-=stride) + // { + // arr[i] += local_array[i]; + // arr[i] *= .5f; + // } + // return true; + //} + return false; +} + +/////////////////////////////////////////////////////// +// manager of possibly-multiple internet audio streams + +LLAudioStreamManagerFMODSTUDIO::LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, const std::string& url) : + mSystem(system), + mStreamChannel(NULL), + mInternetStream(NULL), + mReady(false) +{ + mInternetStreamURL = url; + + FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, 0, &mInternetStream); + + if (result!= FMOD_OK) + { + llwarns << "Couldn't open fmod stream, error " + << FMOD_ErrorString(result) + << llendl; + mReady = false; + return; + } + + mReady = true; +} + +FMOD::Channel *LLAudioStreamManagerFMODSTUDIO::startStream() +{ + // We need a live and opened stream before we try and play it. + if (!mInternetStream || getOpenState() != FMOD_OPENSTATE_READY) + { + llwarns << "No internet stream to start playing!" << llendl; + return NULL; + } + + if(mStreamChannel) + return mStreamChannel; //Already have a channel for this stream. + + mSystem->playSound(mInternetStream, NULL, true, &mStreamChannel); + return mStreamChannel; +} + +bool LLAudioStreamManagerFMODSTUDIO::stopStream() +{ + if (mInternetStream) + { + bool close = true; + switch (getOpenState()) + { + case FMOD_OPENSTATE_CONNECTING: + close = false; + break; + default: + close = true; + } + + if (close && mInternetStream->release() == FMOD_OK) + { + mStreamChannel = NULL; + mInternetStream = NULL; + return true; + } + else + { + return false; + } + } + else + { + return true; + } +} + +FMOD_OPENSTATE LLAudioStreamManagerFMODSTUDIO::getOpenState(unsigned int* percentbuffered, bool* starving, bool* diskbusy) +{ + FMOD_OPENSTATE state; + mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy); + return state; +} + +void LLStreamingAudio_FMODSTUDIO::setBufferSizes(U32 streambuffertime, U32 decodebuffertime) +{ + mSystem->setStreamBufferSize(streambuffertime/1000*128*128, FMOD_TIMEUNIT_RAWBYTES); + FMOD_ADVANCEDSETTINGS settings; + memset(&settings,0,sizeof(settings)); + settings.cbSize=sizeof(settings); + settings.defaultDecodeBufferSize = decodebuffertime;//ms + mSystem->setAdvancedSettings(&settings); +} diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.h b/indra/llaudio/llstreamingaudio_fmodstudio.h new file mode 100644 index 000000000..142256339 --- /dev/null +++ b/indra/llaudio/llstreamingaudio_fmodstudio.h @@ -0,0 +1,87 @@ +/** + * @file streamingaudio_fmodstudio.h + * @author Tofu Linden + * @brief Definition of LLStreamingAudio_FMODSTUDIO implementation + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_STREAMINGAUDIO_FMODSTUDIO_H +#define LL_STREAMINGAUDIO_FMODSTUDIO_H + +#include "stdtypes.h" // from llcommon + +#include "llstreamingaudio.h" +#include "lltimer.h" + +//Stubs +class LLAudioStreamManagerFMODSTUDIO; +namespace FMOD +{ + class System; + class Channel; +} + +//Interfaces +class LLStreamingAudio_FMODSTUDIO : public LLStreamingAudioInterface +{ + public: + LLStreamingAudio_FMODSTUDIO(FMOD::System *system); + /*virtual*/ ~LLStreamingAudio_FMODSTUDIO(); + + /*virtual*/ void start(const std::string& url); + /*virtual*/ void stop(); + /*virtual*/ void pause(int pause); + /*virtual*/ void update(); + /*virtual*/ int isPlaying(); + /*virtual*/ void setGain(F32 vol); + /*virtual*/ F32 getGain(); + /*virtual*/ std::string getURL(); + + /*virtual*/ bool supportsMetaData(){return true;} + /*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not playing. + /*virtual*/ bool supportsWaveData(){return false;} + /*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1); + /*virtual*/ bool supportsAdjustableBufferSizes(){return true;} + /*virtual*/ void setBufferSizes(U32 streambuffertime, U32 decodebuffertime); +private: + FMOD::System *mSystem; + + LLAudioStreamManagerFMODSTUDIO *mCurrentInternetStreamp; + FMOD::Channel *mFMODInternetStreamChannelp; + std::list mDeadStreams; + + std::string mURL; + std::string mPendingURL; + F32 mGain; + + LLSD *mMetaData; +}; + + +#endif // LL_STREAMINGAUDIO_FMOD_H diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index 998b78750..ec5415e50 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -78,7 +78,7 @@ const F32 DEG_TO_RAD = 0.017453292519943295769236907684886f; const F32 RAD_TO_DEG = 57.295779513082320876798154814105f; const F32 F_APPROXIMATELY_ZERO = 0.00001f; const F32 F_LN10 = 2.3025850929940456840179914546844f; -const F32 OO_LN10 = 0.43429448190325182765112891891661; +const F32 OO_LN10 = 0.4342944819032518276511289189166f; const F32 F_LN2 = 0.69314718056f; const F32 OO_LN2 = 1.4426950408889634073599246810019f; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 4668e2c97..244c8ed8c 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -7,13 +7,12 @@ include(Boost) include(DBusGlib) include(DirectX) include(ELFIO) +if(FMODSTUDIO) + include(FMODSTUDIO) +endif(FMODSTUDIO) if(FMODEX) include(FMODEX) - set(FMOD OFF) endif(FMODEX) -if(FMOD) - include(FMOD) -endif(FMOD) include(OPENAL) include(FindOpenGL) include(Hunspell) @@ -1302,12 +1301,13 @@ if (WINDOWS) winspool ) + if(FMODSTUDIO) + list(APPEND viewer_LIBRARIES ${FMODSTUDIO_LIBRARY}) + endif(FMODSTUDIO) + if(FMODEX) list(APPEND viewer_LIBRARIES ${FMODEX_LIBRARY}) endif(FMODEX) - if(FMOD) - list(APPEND viewer_LIBRARIES ${FMOD_LIBRARY}) - endif(FMOD) find_library(INTEL_MEMOPS_LIBRARY NAMES ll_intel_memops @@ -1407,37 +1407,15 @@ if (OPENAL) set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_OPENAL") endif (OPENAL) -if (FMOD OR FMODEX) - if (FMODEX) - set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODEX") - endif (FMODEX) - if (FMOD) - set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMOD") - endif (FMOD) +if (FMODSTUDIO) + set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODSTUDIO") + set(FMODWRAPPER_LIBRARY ${FMODSTUDIO_LIBRARY}) +endif (FMODSTUDIO) - if (DARWIN AND FMOD) - set(fmodwrapper_SOURCE_FILES fmodwrapper.cpp) - add_library(fmodwrapper SHARED ${fmodwrapper_SOURCE_FILES}) - set(fmodwrapper_needed_LIBRARIES ${FMOD_LIBRARY} ${CARBON_LIBRARY}) - set_target_properties( - fmodwrapper - PROPERTIES - BUILD_WITH_INSTALL_RPATH 1 - INSTALL_NAME_DIR "@executable_path/../Resources" - LINK_FLAGS "-unexported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/fmod_hidden_symbols.exp" - ) - set(FMODWRAPPER_LIBRARY fmodwrapper) - target_link_libraries(fmodwrapper ${fmodwrapper_needed_LIBRARIES}) - else (DARWIN AND FMOD) - # fmodwrapper unnecessary on linux or windows, for fmod and fmodex - if (FMODEX) - set(FMODWRAPPER_LIBRARY ${FMODEX_LIBRARY}) - endif (FMODEX) - if (FMOD) - set(FMODWRAPPER_LIBRARY ${FMOD_LIBRARY}) - endif (FMOD) - endif (DARWIN AND FMOD) -endif (FMOD OR FMODEX) +if (FMODEX) + set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODEX") + set(FMODWRAPPER_LIBRARY ${FMODEX_LIBRARY}) +endif (FMODEX) set_source_files_properties(llstartup.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}") @@ -1462,19 +1440,27 @@ set(PACKAGE OFF CACHE BOOL if (WINDOWS) set(release_flags "/MAPRelease/${VIEWER_BINARY_NAME}.map") - if (FMOD) + if (FMODSTUDIO) + if (WORD_SIZE EQUAL 32) + set(fmodstudio_dll_file "fmod.dll") + else (WORD_SIZE EQUAL 32) + set(fmodstudio_dll_file "fmod64.dll") + endif (WORD_SIZE EQUAL 32) + if(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${FMOD_BINARY_DIR}/fmod.dll") + set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${FMODSTUDIO_BINARY_DIR}/${fmodstudio_dll_file}") else(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "--extra_libraries=${FMOD_BINARY_DIR}/fmod.dll") + set(MANIFEST_LIBRARIES "--extra_libraries=${FMODSTUDIO_BINARY_DIR}/${fmodstudio_dll_file}") endif(MANIFEST_LIBRARIES) - endif (FMOD) + set(EXTRA_LINKER_FLAGS "/DELAYLOAD:${fmodstudio_dll_file}") + endif (FMODSTUDIO) + if (FMODEX) - if (WORD_SIZE EQUAL 32) - set(fmodex_dll_file "fmodex.dll") - else (WORD_SIZE EQUAL 32) - set(fmodex_dll_file "fmodex64.dll") - endif (WORD_SIZE EQUAL 32) + if (WORD_SIZE EQUAL 32) + set(fmodex_dll_file "fmodex.dll") + else (WORD_SIZE EQUAL 32) + set(fmodex_dll_file "fmodex64.dll") + endif (WORD_SIZE EQUAL 32) if(MANIFEST_LIBRARIES) set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${FMODEX_BINARY_DIR}/${fmodex_dll_file}") @@ -1651,6 +1637,14 @@ if (LINUX) set(product ${VIEWER_BRANDING_NAME_CAMELCASE}-${ARCH}-${viewer_VERSION}) + if (FMODSTUDIO) + if(MANIFEST_LIBRARIES) + set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${FMODSTUDIO_LIBRARY}") + else(MANIFEST_LIBRARIES) + set(MANIFEST_LIBRARIES "--extra_libraries=${FMODSTUDIO_LIBRARY}") + endif(MANIFEST_LIBRARIES) + endif (FMODSTUDIO) + if (FMODEX) if(MANIFEST_LIBRARIES) set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${FMODEX_LIBRARY}") @@ -1751,6 +1745,15 @@ if (DARWIN) add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_webkit basic_plugin_filepicker) + if (FMODSTUDIO) + add_custom_command(OUTPUT "${FMODSTUDIO_LIBRARY}" + COMMAND cp "${FMODSTUDIO_ORIG_LIBRARY}" "${FMODSTUDIO_LIBRARY}" + COMMAND install_name_tool -id "@executable_path/../Resources/libfmod.dylib" ${FMODSTUDIO_LIBRARY} + DEPENDS "${FMODSTUDIO_ORIG_LIBRARY}") + add_custom_target(fmodstudio_modified_library DEPENDS "${FMODSTUDIO_LIBRARY}") + add_dependencies(${VIEWER_BINARY_NAME} fmodstudio_modified_library) + endif (FMODSTUDIO) + if (FMODEX) add_custom_command(OUTPUT "${FMODEX_LIBRARY}" COMMAND cp "${FMODEX_ORIG_LIBRARY}" "${FMODEX_LIBRARY}" diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 3621179de..dc1e19f1f 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -44,12 +44,13 @@ #include "llviewermedia_streamingaudio.h" #include "llaudioengine.h" -#if LL_FMODEX -# include "llaudioengine_fmodex.h" + +#if LL_FMODSTUDIO +# include "llaudioengine_fmodstudio.h" #endif -#if LL_FMOD -# include "llaudioengine_fmod.h" +#if LL_FMODEX +# include "llaudioengine_fmodex.h" #endif #ifdef LL_OPENAL @@ -415,6 +416,17 @@ void init_audio() { gAudiop = NULL; +#ifdef LL_FMODSTUDIO + if (!gAudiop +#if !LL_WINDOWS + && NULL == getenv("LL_BAD_FMODSTUDIO_DRIVER") +#endif // !LL_WINDOWS + ) + { + gAudiop = (LLAudioEngine *) new LLAudioEngine_FMODSTUDIO(gSavedSettings.getBOOL("SHEnableFMODExProfiler"), gSavedSettings.getBOOL("SHEnableFMODEXVerboseDebugging")); + } +#endif + #ifdef LL_FMODEX if (!gAudiop #if !LL_WINDOWS @@ -437,17 +449,6 @@ void init_audio() } #endif -#ifdef LL_FMOD - if (!gAudiop -#if !LL_WINDOWS - && NULL == getenv("LL_BAD_FMOD_DRIVER") -#endif // !LL_WINDOWS - ) - { - gAudiop = (LLAudioEngine *) new LLAudioEngine_FMOD(); - } -#endif - if (gAudiop) { #if LL_WINDOWS From 2a64c0721563f59b553cdf97fd8c7aea34e2eaf4 Mon Sep 17 00:00:00 2001 From: Drake Arconis Date: Thu, 21 Aug 2014 22:00:59 -0400 Subject: [PATCH 02/55] Fix fmodex breakage --- indra/llaudio/llaudioengine_fmodex.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llaudio/llaudioengine_fmodex.h b/indra/llaudio/llaudioengine_fmodex.h index 18b4d61a8..fe93d8c48 100644 --- a/indra/llaudio/llaudioengine_fmodex.h +++ b/indra/llaudio/llaudioengine_fmodex.h @@ -35,7 +35,7 @@ #define LL_AUDIOENGINE_FMODEX_H #include "llaudioengine.h" -#include "lllistener_fmod.h" +#include "lllistener_fmodex.h" #include "llwindgen.h" //Stubs From 948ebe52135514b584fbc07c30a93ad41814640b Mon Sep 17 00:00:00 2001 From: Drake Arconis Date: Fri, 22 Aug 2014 00:15:09 -0400 Subject: [PATCH 03/55] All your codebase are belong to us. --- indra/cmake/00-Common.cmake | 25 +++-- indra/cmake/Colladadom.cmake | 24 ++++- indra/cmake/CopyWinLibs.cmake | 8 +- indra/cmake/EXPAT.cmake | 6 +- indra/cmake/FreeType.cmake | 6 +- indra/cmake/JsonCpp.cmake | 12 ++- indra/cmake/PNG.cmake | 6 +- indra/develop.py | 8 +- indra/libhacd/hacdRaycastMesh.cpp | 4 + indra/llcharacter/llkeyframemotion.cpp | 2 +- indra/llcharacter/llmotioncontroller.cpp | 6 +- indra/llcommon/llerror.cpp | 2 +- indra/llcommon/lleventcoro.h | 2 +- indra/llimage/llimage.cpp | 16 +-- indra/llimage/llpngwrapper.h | 4 + indra/llmath/llmath.h | 86 ++++++--------- indra/llmath/llquantize.h | 4 +- indra/llmath/llvolume.cpp | 2 +- indra/llmath/v4color.cpp | 8 +- indra/llmath/v4coloru.h | 22 ++-- indra/llmessage/aicurlprivate.h | 2 +- indra/llmessage/llregionhandle.h | 4 +- indra/llmessage/llthrottle.cpp | 2 +- indra/llmessage/message.cpp | 2 +- indra/llprimitive/llmaterial.cpp | 20 ++-- indra/llprimitive/llprimitive.cpp | 16 +-- indra/llprimitive/llvolumemessage.cpp | 64 ++++++------ indra/llrender/llfontfreetype.cpp | 4 +- indra/llrender/llfontgl.cpp | 38 +++---- indra/llrender/llpostprocess.cpp | 4 +- indra/llrender/llrender2dutils.cpp | 16 +-- indra/llrender/lluiimage.cpp | 4 +- indra/llui/llbutton.cpp | 8 +- indra/llui/llcheckboxctrl.cpp | 4 +- indra/llui/lldraghandle.cpp | 2 +- indra/llui/llfloater.cpp | 18 ++-- indra/llui/llfocusmgr.h | 2 +- indra/llui/lllayoutstack.cpp | 24 ++--- indra/llui/lllineeditor.cpp | 22 ++-- indra/llui/llmenugl.cpp | 10 +- indra/llui/llmodaldialog.cpp | 2 +- indra/llui/llprogressbar.cpp | 2 +- indra/llui/llscrollcontainer.cpp | 2 +- indra/llui/llscrolllistcell.cpp | 4 +- indra/llui/llscrolllistctrl.cpp | 4 +- indra/llui/llspinctrl.cpp | 6 +- indra/llui/llstatgraph.cpp | 2 +- indra/llui/lltexteditor.cpp | 26 ++--- indra/llui/llui.cpp | 16 +-- indra/llui/llurlentry.cpp | 6 +- indra/newview/aihttpview.cpp | 2 +- indra/newview/llcloud.cpp | 2 +- indra/newview/lldrawable.cpp | 2 +- indra/newview/lldrawpoolbump.cpp | 4 +- indra/newview/llfasttimerview.cpp | 10 +- indra/newview/llflexibleobject.cpp | 2 +- indra/newview/llfloaterbuyland.cpp | 4 +- indra/newview/llfloaterenvsettings.cpp | 2 +- indra/newview/llfloaterland.cpp | 8 +- indra/newview/llfloaterlandholdings.cpp | 4 +- .../llfloaterpathfindingcharacters.cpp | 2 +- indra/newview/llfloatersnapshot.cpp | 52 +++++----- indra/newview/llfloaterworldmap.cpp | 6 +- indra/newview/llfolderview.cpp | 4 +- indra/newview/llfolderviewitem.cpp | 16 +-- indra/newview/llglsandbox.cpp | 8 +- indra/newview/llmanip.cpp | 4 +- indra/newview/llmaniprotate.cpp | 10 +- indra/newview/llmanipscale.cpp | 14 +-- indra/newview/llmaniptranslate.cpp | 8 +- indra/newview/llmediactrl.cpp | 26 ++--- indra/newview/llnetmap.cpp | 56 +++++----- indra/newview/llpanelclassified.cpp | 12 +-- indra/newview/llpaneldisplay.cpp | 4 +- indra/newview/llpanelevent.cpp | 6 +- indra/newview/llpanelgrouplandmoney.cpp | 4 +- indra/newview/llpanelmaininventory.cpp | 2 +- indra/newview/llpanelobject.cpp | 12 +-- indra/newview/llpanelpick.cpp | 12 +-- indra/newview/llpanelplace.cpp | 12 +-- indra/newview/llpanelprimmediacontrols.cpp | 8 +- indra/newview/llpreviewtexture.cpp | 4 +- indra/newview/llslurl.cpp | 24 ++--- indra/newview/llstartup.cpp | 4 +- indra/newview/llsurface.cpp | 12 +-- indra/newview/lltexturectrl.cpp | 2 +- indra/newview/lltoolselectland.cpp | 4 +- indra/newview/llviewerdisplay.cpp | 8 +- indra/newview/llviewerkeyboard.cpp | 6 +- indra/newview/llviewermedia.cpp | 6 +- indra/newview/llviewermessage.cpp | 6 +- indra/newview/llviewerparcelmgr.cpp | 18 ++-- indra/newview/llviewerpartsim.cpp | 2 +- indra/newview/llviewerpartsource.cpp | 2 +- indra/newview/llviewertextureanim.cpp | 2 +- indra/newview/llviewerwindow.cpp | 50 ++++----- indra/newview/llvlcomposition.cpp | 12 +-- indra/newview/llvoavatar.cpp | 2 +- indra/newview/llvoicevivox.cpp | 2 +- indra/newview/llvopartgroup.cpp | 2 +- indra/newview/llvovolume.cpp | 10 +- indra/newview/llwlanimator.cpp | 2 +- indra/newview/llworld.cpp | 2 +- indra/newview/llworldmapview.cpp | 62 +++++------ indra/newview/viewer_manifest.py | 5 +- indra/tools/vstool/VSTool.csproj | 5 +- indra/tools/vstool/VSTool.exe | Bin 24576 -> 24576 bytes indra/tools/vstool/VSTool.sln | 6 +- indra/tools/vstool/main.cs | 7 +- install.xml | 98 ++++++++++-------- 110 files changed, 661 insertions(+), 611 deletions(-) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index efffa89b0..a940163e7 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -45,18 +45,17 @@ if (WINDOWS) if (MSVC10) set(MSVC_DIR 10.0) set(MSVC_SUFFIX 100) + elseif (MSVC12) + set(MSVC_DIR 12.0) + set(MSVC_SUFFIX 120) endif (MSVC10) - if (MSVC11) - set(MSVC_DIR 11.0) - set(MSVC_SUFFIX 110) - endif (MSVC11) # Remove default /Zm1000 flag that cmake inserts string (REPLACE "/Zm1000" " " CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - if (MSVC11) + if (MSVC12) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zm140") - endif (MSVC11) + endif (MSVC12) # Don't build DLLs. @@ -87,7 +86,7 @@ if (WINDOWS) /W3 /c /Zc:forScope - /Zc:wchar_t- + /Zc:wchar_t- /nologo /Oy- ) @@ -99,9 +98,15 @@ if (WINDOWS) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /wd4267 /wd4250 /wd4244") endif(WORD_SIZE EQUAL 32) - # configure win32 API for windows XP+ compatibility - set(WINVER "0x0501" CACHE STRING "Win32 API Target version (see http://msdn.microsoft.com/en-us/library/aa383745%28v=VS.85%29.aspx)") - add_definitions("/DWINVER=${WINVER}" "/D_WIN32_WINNT=${WINVER}") + if (MSVC12) + # configure win32 API for windows vista+ compatibility + set(WINVER "0x0600" CACHE STRING "Win32 API Target version (see http://msdn.microsoft.com/en-us/library/aa383745%28v=VS.85%29.aspx)") + add_definitions("/DWINVER=${WINVER}" "/D_WIN32_WINNT=${WINVER}") + else (MSVC12) + # configure win32 API for windows XP+ compatibility + set(WINVER "0x0501" CACHE STRING "Win32 API Target version (see http://msdn.microsoft.com/en-us/library/aa383745%28v=VS.85%29.aspx)") + add_definitions("/DWINVER=${WINVER}" "/D_WIN32_WINNT=${WINVER}") + endif (MSVC12) # Are we using the crummy Visual Studio KDU build workaround? if (NOT DISABLE_FATAL_WARNINGS) diff --git a/indra/cmake/Colladadom.cmake b/indra/cmake/Colladadom.cmake index e503d99e7..cc4fa50ea 100644 --- a/indra/cmake/Colladadom.cmake +++ b/indra/cmake/Colladadom.cmake @@ -1,6 +1,7 @@ # -*- cmake -*- include(Prebuilt) +include(Boost) set(COLLADADOM_FIND_QUIETLY OFF) set(COLLADADOM_FIND_REQUIRED ON) @@ -24,11 +25,26 @@ else (STANDALONE) ) if (WINDOWS) - add_definitions(-DDOM_DYNAMIC) - set(COLLADADOM_LIBRARIES - debug libcollada14dom22-d - optimized libcollada14dom22 + if(MSVC12) + use_prebuilt_binary(pcre) + use_prebuilt_binary(libxml) + set(COLLADADOM_LIBRARIES + debug libcollada14dom23-sd + optimized libcollada14dom23-s + libxml2_a + debug pcrecppd + optimized pcrecpp + debug pcred + optimized pcre + ${BOOST_SYSTEM_LIBRARIES} ) + else(MSVC12) + add_definitions(-DDOM_DYNAMIC) + set(COLLADADOM_LIBRARIES + debug libcollada14dom22-d + optimized libcollada14dom22 + ) + endif(MSVC12) else (WINDOWS) set(COLLADADOM_LIBRARIES collada14dom diff --git a/indra/cmake/CopyWinLibs.cmake b/indra/cmake/CopyWinLibs.cmake index fb5093f1b..2e6c1fe05 100644 --- a/indra/cmake/CopyWinLibs.cmake +++ b/indra/cmake/CopyWinLibs.cmake @@ -42,9 +42,13 @@ set(debug_files libapriconv-1.dll libeay32.dll ssleay32.dll - libcollada14dom22-d.dll glod.dll ) + if(MSVC10) + set(release_files ${release_files} + libcollada14dom22-d.dll + ) + endif(MSVC10) copy_if_different( ${debug_src_dir} @@ -223,12 +227,12 @@ set(release_files libapriconv-1.dll libeay32.dll ssleay32.dll - libcollada14dom22.dll glod.dll ) if(WORD_SIZE EQUAL 32) set(release_files ${release_files} + libcollada14dom22.dll libtcmalloc_minimal.dll ) endif(WORD_SIZE EQUAL 32) diff --git a/indra/cmake/EXPAT.cmake b/indra/cmake/EXPAT.cmake index 8a8f84526..895e15f0a 100644 --- a/indra/cmake/EXPAT.cmake +++ b/indra/cmake/EXPAT.cmake @@ -9,7 +9,11 @@ if (STANDALONE) else (STANDALONE) use_prebuilt_binary(expat) if (WINDOWS) - set(EXPAT_LIBRARIES libexpatMT) + if (MSVC12) + set(EXPAT_LIBRARIES expat) + else (MSVC12) + set(EXPAT_LIBRARIES libexpatMT) + endif (MSVC12) else (WINDOWS) set(EXPAT_LIBRARIES expat) endif (WINDOWS) diff --git a/indra/cmake/FreeType.cmake b/indra/cmake/FreeType.cmake index e9d4d8093..7ede1c749 100644 --- a/indra/cmake/FreeType.cmake +++ b/indra/cmake/FreeType.cmake @@ -11,7 +11,11 @@ else (STANDALONE) set(FREETYPE_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) else (LINUX) - set(FREETYPE_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + if(MSVC12) + set(FREETYPE_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/freetype2) + else(MSVC12) + set(FREETYPE_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + endif (MSVC12) endif (LINUX) set(FREETYPE_LIBRARIES freetype) diff --git a/indra/cmake/JsonCpp.cmake b/indra/cmake/JsonCpp.cmake index 499089843..f088087cf 100644 --- a/indra/cmake/JsonCpp.cmake +++ b/indra/cmake/JsonCpp.cmake @@ -10,9 +10,15 @@ if (STANDALONE) else (STANDALONE) use_prebuilt_binary(jsoncpp) if (WINDOWS) - set(JSONCPP_LIBRARIES - debug json_vc${MSVC_SUFFIX}d - optimized json_vc${MSVC_SUFFIX}) + if(MSVC12) + set(JSONCPP_LIBRARIES + debug json_vc${MSVC_SUFFIX}debug_libmt.lib + optimized json_vc${MSVC_SUFFIX}_libmt) + else(MSVC12) + set(JSONCPP_LIBRARIES + debug json_vc${MSVC_SUFFIX}d + optimized json_vc${MSVC_SUFFIX}) + endif(MSVC12) elseif (DARWIN) set(JSONCPP_LIBRARIES json_linux-gcc-4.0.1_libmt) elseif (LINUX) diff --git a/indra/cmake/PNG.cmake b/indra/cmake/PNG.cmake index 011b87f64..c82e9e06b 100644 --- a/indra/cmake/PNG.cmake +++ b/indra/cmake/PNG.cmake @@ -9,7 +9,11 @@ if (STANDALONE) else (STANDALONE) use_prebuilt_binary(libpng) if (WINDOWS) - set(PNG_LIBRARIES libpng15) + if(MSVC12) + set(PNG_LIBRARIES libpng16) + else(MSVC12) + set(PNG_LIBRARIES libpng15) + endif(MSVC12) elseif(DARWIN) set(PNG_LIBRARIES png15) else(LINUX) diff --git a/indra/develop.py b/indra/develop.py index 0c732cc02..40c4fe459 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -444,14 +444,14 @@ class WindowsSetup(PlatformSetup): 'gen' : r'Visual Studio 10', 'ver' : r'10.0' }, - 'vc110' : { - 'gen' : r'Visual Studio 11', - 'ver' : r'11.0' + 'vc120' : { + 'gen' : r'Visual Studio 12', + 'ver' : r'12.0' } } gens['vs2010'] = gens['vc100'] - gens['vs2012'] = gens['vc110'] + gens['vs2013'] = gens['vc120'] search_path = r'C:\windows' exe_suffixes = ('.exe', '.bat', '.com') diff --git a/indra/libhacd/hacdRaycastMesh.cpp b/indra/libhacd/hacdRaycastMesh.cpp index 2f84bbc11..28e1325a1 100644 --- a/indra/libhacd/hacdRaycastMesh.cpp +++ b/indra/libhacd/hacdRaycastMesh.cpp @@ -17,6 +17,10 @@ #include #include #include "hacdManifoldMesh.h" + +#if _MSC_VER >= 1800 +#include +#endif namespace HACD { bool BBox::Raycast(const Vec3 & origin, const Vec3 & dir, Float & distMin) const diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp index 711ac21e5..4be835dce 100644 --- a/indra/llcharacter/llkeyframemotion.cpp +++ b/indra/llcharacter/llkeyframemotion.cpp @@ -1111,7 +1111,7 @@ void LLKeyframeMotion::applyConstraint(JointConstraint* constraint, F32 time, U8 LLVector3 source_to_target = target_pos - keyframe_source_pos; - S32 max_iteration_count = llround(clamp_rescale( + S32 max_iteration_count = llmath::llround(clamp_rescale( mCharacter->getPixelArea(), MAX_PIXEL_AREA_CONSTRAINTS, MIN_PIXEL_AREA_CONSTRAINTS, diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index d12b3ddb6..2764405e5 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -876,12 +876,12 @@ void LLMotionController::updateMotions(bool force_update) // // This old code is nonsense. - //S32 quantum_count = llmax(0, llround((update_time - time_interval) / mTimeStep)) + 1; + //S32 quantum_count = llmax(0, llmath::llround((update_time - time_interval) / mTimeStep)) + 1; // (update_time - time_interval) / mTimeStep is an integer! We need llround to get rid of floating point errors, not llfloor. - // Moreover, just rounding off to the nearest integer with llround(update_time / mTimeStep) makes a lot more sense: + // Moreover, just rounding off to the nearest integer with llmath::llround(update_time / mTimeStep) makes a lot more sense: // it is the best we can do to get as close to what we should draw as possible. // However, mAnimTime may only be incremented; therefore make sure of that with the llmax. - S32 quantum_count = llmax(llround(update_time / mTimeStep), llceil(mAnimTime / mTimeStep)); + S32 quantum_count = llmax(llmath::llround(update_time / mTimeStep), llceil(mAnimTime / mTimeStep)); // if (quantum_count == mTimeStepCount) { diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 8b40edb40..b495f4a4b 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -108,7 +108,7 @@ namespace { mFile.close(); } - bool okay() { return mFile; } + bool okay() { return !!mFile; } virtual bool wantsTime() { return true; } diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index a42af63b6..b451092bf 100644 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -67,7 +67,7 @@ public: LLEventPumpOrPumpName() {} operator LLEventPump& () const { return *mPump; } LLEventPump& getPump() const { return *mPump; } - operator bool() const { return mPump; } + operator bool() const { return !!mPump; } bool operator!() const { return ! mPump; } private: diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 2e0531263..18258ad3a 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -1178,13 +1178,13 @@ void LLImageRaw::copyLineScaled( U8* in, U8* out, S32 in_pixel_len, S32 out_pixe a *= norm_factor; // skip conditional S32 t4 = x * out_pixel_step * components; - out[t4 + 0] = U8(llround(r)); + out[t4 + 0] = U8(llmath::llround(r)); if (components >= 2) - out[t4 + 1] = U8(llround(g)); + out[t4 + 1] = U8(llmath::llround(g)); if (components >= 3) - out[t4 + 2] = U8(llround(b)); + out[t4 + 2] = U8(llmath::llround(b)); if( components == 4) - out[t4 + 3] = U8(llround(a)); + out[t4 + 3] = U8(llmath::llround(a)); } } } @@ -1259,10 +1259,10 @@ void LLImageRaw::compositeRowScaled4onto3( U8* in, U8* out, S32 in_pixel_len, S3 b *= norm_factor; a *= norm_factor; - in_scaled_r = U8(llround(r)); - in_scaled_g = U8(llround(g)); - in_scaled_b = U8(llround(b)); - in_scaled_a = U8(llround(a)); + in_scaled_r = U8(llmath::llround(r)); + in_scaled_g = U8(llmath::llround(g)); + in_scaled_b = U8(llmath::llround(b)); + in_scaled_a = U8(llmath::llround(a)); } if( in_scaled_a ) diff --git a/indra/llimage/llpngwrapper.h b/indra/llimage/llpngwrapper.h index 5f3ad0d5a..d5eff1a8e 100644 --- a/indra/llimage/llpngwrapper.h +++ b/indra/llimage/llpngwrapper.h @@ -30,8 +30,12 @@ #include #else // Workaround for wrongly packaged prebuilt. +#if _MSC_VER >= 1800 +#include +#else #include "libpng15/png.h" #endif +#endif #include "llimage.h" class LLPngWrapper diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index ec5415e50..e56568adb 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -174,6 +174,7 @@ inline S32 lltrunc( F32 f ) inline S32 lltrunc( F64 f ) { + return (S32)f; } @@ -204,64 +205,35 @@ inline S32 llceil( F32 f ) } -#ifndef BOGUS_ROUND -// Use this round. Does an arithmetic round (0.5 always rounds up) -inline S32 llround(const F32 val) +namespace llmath { - return llfloor(val + 0.5f); -} - -#else // BOGUS_ROUND -// Old llround implementation - does banker's round (toward nearest even in the case of a 0.5. -// Not using this because we don't have a consistent implementation on both platforms, use -// llfloor(val + 0.5f), which is consistent on all platforms. -inline S32 llround(const F32 val) -{ - #if LL_WINDOWS - // Note: assumes that the floating point control word is set to rounding mode (the default) - S32 ret_val; - _asm fld val - _asm fistp ret_val; - return ret_val; - #elif LL_LINUX - // Note: assumes that the floating point control word is set - // to rounding mode (the default) - S32 ret_val; - __asm__ __volatile__( "flds %1 \n\t" - "fistpl %0 \n\t" - : "=m" (ret_val) - : "m" (val) ); - return ret_val; - #else - return llfloor(val + 0.5f); - #endif -} - -// A fast arithmentic round on intel, from Laurent de Soras http://ldesoras.free.fr -inline int round_int(double x) -{ - const float round_to_nearest = 0.5f; - int i; - __asm + // Use this round. Does an arithmetic round (0.5 always rounds up) + inline S32 llround(const F32 val) { - fld x - fadd st, st (0) - fadd round_to_nearest - fistp i - sar i, 1 +#if __cplusplus >= 201103L || _MSC_VER >= 1800 + return std::round(val); +#else + return llfloor(val + 0.5f); +#endif } - return (i); -} -#endif // BOGUS_ROUND -inline F32 llround( F32 val, F32 nearest ) -{ - return F32(floor(val * (1.0f / nearest) + 0.5f)) * nearest; -} + inline F32 llround(F32 val, F32 nearest) + { +#if __cplusplus >= 201103L || _MSC_VER >= 1800 + return F32(std::round(val * (1.0f / nearest))) * nearest; +#else + return F32(floor(val * (1.0f / nearest) + 0.5f)) * nearest; +#endif + } -inline F64 llround( F64 val, F64 nearest ) -{ - return F64(floor(val * (1.0 / nearest) + 0.5)) * nearest; + inline F64 llround(F64 val, F64 nearest) + { +#if __cplusplus >= 201103L || _MSC_VER >= 1800 + return F64(std::round(val * (1.0 / nearest))) * nearest; +#else + return F64(floor(val * (1.0 / nearest) + 0.5)) * nearest; +#endif + } } // these provide minimum peak error @@ -309,7 +281,7 @@ const S32 LL_SHIFT_AMOUNT = 16; //16.16 fixed point represe #define LL_MAN_INDEX 1 #endif -/* Deprecated: use llround(), lltrunc(), or llfloor() instead +/* Deprecated: use llmath::llround(), lltrunc(), or llfloor() instead // ================================================================================================ // Real2Int // ================================================================================================ @@ -351,7 +323,7 @@ static union #define LL_EXP_A (1048576 * OO_LN2) // use 1512775 for integer #define LL_EXP_C (60801) // this value of C good for -4 < y < 4 -#define LL_FAST_EXP(y) (LLECO.n.i = llround(F32(LL_EXP_A*(y))) + (1072693248 - LL_EXP_C), LLECO.d) +#define LL_FAST_EXP(y) (LLECO.n.i = llmath::llround(F32(LL_EXP_A*(y))) + (1072693248 - LL_EXP_C), LLECO.d) @@ -370,8 +342,8 @@ inline F32 snap_to_sig_figs(F32 foo, S32 sig_figs) bar *= 10.f; } - //F32 new_foo = (F32)llround(foo * bar); - // the llround() implementation sucks. Don't us it. + //F32 new_foo = (F32)llmath::llround(foo * bar); + // the llmath::llround() implementation sucks. Don't us it. F32 sign = (foo > 0.f) ? 1.f : -1.f; F32 new_foo = F32( S64(foo * bar + sign * 0.5f)); diff --git a/indra/llmath/llquantize.h b/indra/llmath/llquantize.h index dd0cf2941..6df9c1fb4 100644 --- a/indra/llmath/llquantize.h +++ b/indra/llmath/llquantize.h @@ -52,7 +52,7 @@ inline U16 F32_to_U16_ROUND(F32 val, F32 lower, F32 upper) val /= (upper - lower); // round the value. Sreturn the U16 - return (U16)(llround(val*U16MAX)); + return (U16)(llmath::llround(val*U16MAX)); } @@ -92,7 +92,7 @@ inline U8 F32_to_U8_ROUND(F32 val, F32 lower, F32 upper) val /= (upper - lower); // return the rounded U8 - return (U8)(llround(val*U8MAX)); + return (U8)(llmath::llround(val*U8MAX)); } diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 3b5b2f148..25697eef6 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -560,7 +560,7 @@ void LLProfile::genNGon(const LLProfileParams& params, S32 sides, F32 offset, F3 // Scale to have size "match" scale. Compensates to get object to generally fill bounding box. - S32 total_sides = llround(sides / ang_scale); // Total number of sides all around + S32 total_sides = llmath::llround(sides / ang_scale); // Total number of sides all around if (total_sides < 8) { diff --git a/indra/llmath/v4color.cpp b/indra/llmath/v4color.cpp index 9131befd8..6b771c3ad 100644 --- a/indra/llmath/v4color.cpp +++ b/indra/llmath/v4color.cpp @@ -125,10 +125,10 @@ LLColor4 LLColor4::cyan6(0.2f, 0.6f, 0.6f, 1.0f); LLColor4::operator const LLColor4U() const { return LLColor4U( - (U8)llclampb(llround(mV[VRED]*255.f)), - (U8)llclampb(llround(mV[VGREEN]*255.f)), - (U8)llclampb(llround(mV[VBLUE]*255.f)), - (U8)llclampb(llround(mV[VALPHA]*255.f))); + (U8)llclampb(llmath::llround(mV[VRED]*255.f)), + (U8)llclampb(llmath::llround(mV[VGREEN]*255.f)), + (U8)llclampb(llmath::llround(mV[VBLUE]*255.f)), + (U8)llclampb(llmath::llround(mV[VALPHA]*255.f))); } LLColor4::LLColor4(const LLColor3 &vec, F32 a) diff --git a/indra/llmath/v4coloru.h b/indra/llmath/v4coloru.h index fa1295441..5040f0710 100644 --- a/indra/llmath/v4coloru.h +++ b/indra/llmath/v4coloru.h @@ -353,10 +353,10 @@ inline LLColor4U LLColor4U::multAll(const F32 k) { // Round to nearest return LLColor4U( - (U8)llround(mV[VX] * k), - (U8)llround(mV[VY] * k), - (U8)llround(mV[VZ] * k), - (U8)llround(mV[VW] * k)); + (U8)llmath::llround(mV[VX] * k), + (U8)llmath::llround(mV[VY] * k), + (U8)llmath::llround(mV[VZ] * k), + (U8)llmath::llround(mV[VW] * k)); } /* inline LLColor4U operator*(const LLColor4U &a, U8 k) @@ -471,7 +471,7 @@ void LLColor4U::setVecScaleClamp(const LLColor4& color) color_scale_factor /= max_color; } const S32 MAX_COLOR = 255; - S32 r = llround(color.mV[0] * color_scale_factor); + S32 r = llmath::llround(color.mV[0] * color_scale_factor); if (r > MAX_COLOR) { r = MAX_COLOR; @@ -482,7 +482,7 @@ void LLColor4U::setVecScaleClamp(const LLColor4& color) } mV[0] = r; - S32 g = llround(color.mV[1] * color_scale_factor); + S32 g = llmath::llround(color.mV[1] * color_scale_factor); if (g > MAX_COLOR) { g = MAX_COLOR; @@ -493,7 +493,7 @@ void LLColor4U::setVecScaleClamp(const LLColor4& color) } mV[1] = g; - S32 b = llround(color.mV[2] * color_scale_factor); + S32 b = llmath::llround(color.mV[2] * color_scale_factor); if (b > MAX_COLOR) { b = MAX_COLOR; @@ -505,7 +505,7 @@ void LLColor4U::setVecScaleClamp(const LLColor4& color) mV[2] = b; // Alpha shouldn't be scaled, just clamped... - S32 a = llround(color.mV[3] * MAX_COLOR); + S32 a = llmath::llround(color.mV[3] * MAX_COLOR); if (a > MAX_COLOR) { a = MAX_COLOR; @@ -527,7 +527,7 @@ void LLColor4U::setVecScaleClamp(const LLColor3& color) } const S32 MAX_COLOR = 255; - S32 r = llround(color.mV[0] * color_scale_factor); + S32 r = llmath::llround(color.mV[0] * color_scale_factor); if (r > MAX_COLOR) { r = MAX_COLOR; @@ -539,7 +539,7 @@ void LLColor4U::setVecScaleClamp(const LLColor3& color) } mV[0] = r; - S32 g = llround(color.mV[1] * color_scale_factor); + S32 g = llmath::llround(color.mV[1] * color_scale_factor); if (g > MAX_COLOR) { g = MAX_COLOR; @@ -551,7 +551,7 @@ void LLColor4U::setVecScaleClamp(const LLColor3& color) } mV[1] = g; - S32 b = llround(color.mV[2] * color_scale_factor); + S32 b = llmath::llround(color.mV[2] * color_scale_factor); if (b > MAX_COLOR) { b = MAX_COLOR; diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index be3d90e7d..1b8e7fd84 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -466,7 +466,7 @@ class BufferedCurlEasyRequest : public CurlEasyRequest { // Return true when prepRequest was already called and the object has not been // invalidated as a result of calling aborted(). - bool isValid(void) const { return mResponder; } + bool isValid(void) const { return !!mResponder; } // Return the capability type of this request. AICapabilityType capability_type(void) const { llassert(mCapabilityType != number_of_capability_types); return mCapabilityType; } diff --git a/indra/llmessage/llregionhandle.h b/indra/llmessage/llregionhandle.h index c77794e4b..d0c29c54c 100644 --- a/indra/llmessage/llregionhandle.h +++ b/indra/llmessage/llregionhandle.h @@ -73,7 +73,7 @@ inline BOOL to_region_handle(const F32 x_pos, const F32 y_pos, U64 *region_handl } else { - x_int = (U32)llround(x_pos); + x_int = (U32)llmath::llround(x_pos); } if (y_pos < 0.f) { @@ -82,7 +82,7 @@ inline BOOL to_region_handle(const F32 x_pos, const F32 y_pos, U64 *region_handl } else { - y_int = (U32)llround(y_pos); + y_int = (U32)llmath::llround(y_pos); } *region_handle = to_region_handle(x_int, y_int); return TRUE; diff --git a/indra/llmessage/llthrottle.cpp b/indra/llmessage/llthrottle.cpp index 64ebd51fe..b28b9a944 100644 --- a/indra/llmessage/llthrottle.cpp +++ b/indra/llmessage/llthrottle.cpp @@ -391,7 +391,7 @@ BOOL LLThrottleGroup::dynamicAdjust() } mBitsSentThisPeriod[i] = 0; - total += llround(mBitsSentHistory[i]); + total += llmath::llround(mBitsSentHistory[i]); } // Look for busy channels diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index b06751886..03ad0d9d1 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -2765,7 +2765,7 @@ void LLMessageSystem::dumpReceiveCounts() if (mt->mReceiveCount > 0) { LL_INFOS("Messaging") << "Num: " << std::setw(3) << mt->mReceiveCount << " Bytes: " << std::setw(6) << mt->mReceiveBytes - << " Invalid: " << std::setw(3) << mt->mReceiveInvalid << " " << mt->mName << " " << llround(100 * mt->mDecodeTimeThisFrame / mReceiveTime) << "%" << llendl; + << " Invalid: " << std::setw(3) << mt->mReceiveInvalid << " " << mt->mName << " " << llmath::llround(100 * mt->mDecodeTimeThisFrame / mReceiveTime) << "%" << llendl; } } } diff --git a/indra/llprimitive/llmaterial.cpp b/indra/llprimitive/llmaterial.cpp index cf4c645cf..bfe8301a7 100644 --- a/indra/llprimitive/llmaterial.cpp +++ b/indra/llprimitive/llmaterial.cpp @@ -119,18 +119,18 @@ LLSD LLMaterial::asLLSD() const LLSD material_data; material_data[MATERIALS_CAP_NORMAL_MAP_FIELD] = mNormalID; - material_data[MATERIALS_CAP_NORMAL_MAP_OFFSET_X_FIELD] = llround(mNormalOffsetX * MATERIALS_MULTIPLIER); - material_data[MATERIALS_CAP_NORMAL_MAP_OFFSET_Y_FIELD] = llround(mNormalOffsetY * MATERIALS_MULTIPLIER); - material_data[MATERIALS_CAP_NORMAL_MAP_REPEAT_X_FIELD] = llround(mNormalRepeatX * MATERIALS_MULTIPLIER); - material_data[MATERIALS_CAP_NORMAL_MAP_REPEAT_Y_FIELD] = llround(mNormalRepeatY * MATERIALS_MULTIPLIER); - material_data[MATERIALS_CAP_NORMAL_MAP_ROTATION_FIELD] = llround(mNormalRotation * MATERIALS_MULTIPLIER); + material_data[MATERIALS_CAP_NORMAL_MAP_OFFSET_X_FIELD] = llmath::llround(mNormalOffsetX * MATERIALS_MULTIPLIER); + material_data[MATERIALS_CAP_NORMAL_MAP_OFFSET_Y_FIELD] = llmath::llround(mNormalOffsetY * MATERIALS_MULTIPLIER); + material_data[MATERIALS_CAP_NORMAL_MAP_REPEAT_X_FIELD] = llmath::llround(mNormalRepeatX * MATERIALS_MULTIPLIER); + material_data[MATERIALS_CAP_NORMAL_MAP_REPEAT_Y_FIELD] = llmath::llround(mNormalRepeatY * MATERIALS_MULTIPLIER); + material_data[MATERIALS_CAP_NORMAL_MAP_ROTATION_FIELD] = llmath::llround(mNormalRotation * MATERIALS_MULTIPLIER); material_data[MATERIALS_CAP_SPECULAR_MAP_FIELD] = mSpecularID; - material_data[MATERIALS_CAP_SPECULAR_MAP_OFFSET_X_FIELD] = llround(mSpecularOffsetX * MATERIALS_MULTIPLIER); - material_data[MATERIALS_CAP_SPECULAR_MAP_OFFSET_Y_FIELD] = llround(mSpecularOffsetY * MATERIALS_MULTIPLIER); - material_data[MATERIALS_CAP_SPECULAR_MAP_REPEAT_X_FIELD] = llround(mSpecularRepeatX * MATERIALS_MULTIPLIER); - material_data[MATERIALS_CAP_SPECULAR_MAP_REPEAT_Y_FIELD] = llround(mSpecularRepeatY * MATERIALS_MULTIPLIER); - material_data[MATERIALS_CAP_SPECULAR_MAP_ROTATION_FIELD] = llround(mSpecularRotation * MATERIALS_MULTIPLIER); + material_data[MATERIALS_CAP_SPECULAR_MAP_OFFSET_X_FIELD] = llmath::llround(mSpecularOffsetX * MATERIALS_MULTIPLIER); + material_data[MATERIALS_CAP_SPECULAR_MAP_OFFSET_Y_FIELD] = llmath::llround(mSpecularOffsetY * MATERIALS_MULTIPLIER); + material_data[MATERIALS_CAP_SPECULAR_MAP_REPEAT_X_FIELD] = llmath::llround(mSpecularRepeatX * MATERIALS_MULTIPLIER); + material_data[MATERIALS_CAP_SPECULAR_MAP_REPEAT_Y_FIELD] = llmath::llround(mSpecularRepeatY * MATERIALS_MULTIPLIER); + material_data[MATERIALS_CAP_SPECULAR_MAP_ROTATION_FIELD] = llmath::llround(mSpecularRotation * MATERIALS_MULTIPLIER); material_data[MATERIALS_CAP_SPECULAR_COLOR_FIELD] = mSpecularLightColor.getValue(); material_data[MATERIALS_CAP_SPECULAR_EXP_FIELD] = mSpecularLightExponent; diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index bdf15cb94..e9b7b2f42 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -1142,12 +1142,12 @@ BOOL LLPrimitive::packTEMessage(LLMessageSystem *mesgsys) const const LLTextureEntry* te = getTE(face_index); scale_s[face_index] = (F32) te->mScaleS; scale_t[face_index] = (F32) te->mScaleT; - offset_s[face_index] = (S16) llround((llclamp(te->mOffsetS,-1.0f,1.0f) * (F32)0x7FFF)) ; - offset_t[face_index] = (S16) llround((llclamp(te->mOffsetT,-1.0f,1.0f) * (F32)0x7FFF)) ; - image_rot[face_index] = (S16) llround(((fmod(te->mRotation, F_TWO_PI)/F_TWO_PI) * TEXTURE_ROTATION_PACK_FACTOR)); + offset_s[face_index] = (S16) llmath::llround((llclamp(te->mOffsetS,-1.0f,1.0f) * (F32)0x7FFF)) ; + offset_t[face_index] = (S16) llmath::llround((llclamp(te->mOffsetT,-1.0f,1.0f) * (F32)0x7FFF)) ; + image_rot[face_index] = (S16) llmath::llround(((fmod(te->mRotation, F_TWO_PI)/F_TWO_PI) * TEXTURE_ROTATION_PACK_FACTOR)); bump[face_index] = te->getBumpShinyFullbright(); media_flags[face_index] = te->getMediaTexGen(); - glow[face_index] = (U8) llround((llclamp(te->getGlow(), 0.0f, 1.0f) * (F32)0xFF)); + glow[face_index] = (U8) llmath::llround((llclamp(te->getGlow(), 0.0f, 1.0f) * (F32)0xFF)); // Directly sending material_ids is not safe! memcpy(&material_data[face_index*16],getTE(face_index)->getMaterialID().get(),16); /* Flawfinder: ignore */ @@ -1227,12 +1227,12 @@ BOOL LLPrimitive::packTEMessage(LLDataPacker &dp) const const LLTextureEntry* te = getTE(face_index); scale_s[face_index] = (F32) te->mScaleS; scale_t[face_index] = (F32) te->mScaleT; - offset_s[face_index] = (S16) llround((llclamp(te->mOffsetS,-1.0f,1.0f) * (F32)0x7FFF)) ; - offset_t[face_index] = (S16) llround((llclamp(te->mOffsetT,-1.0f,1.0f) * (F32)0x7FFF)) ; - image_rot[face_index] = (S16) llround(((fmod(te->mRotation, F_TWO_PI)/F_TWO_PI) * TEXTURE_ROTATION_PACK_FACTOR)); + offset_s[face_index] = (S16) llmath::llround((llclamp(te->mOffsetS,-1.0f,1.0f) * (F32)0x7FFF)) ; + offset_t[face_index] = (S16) llmath::llround((llclamp(te->mOffsetT,-1.0f,1.0f) * (F32)0x7FFF)) ; + image_rot[face_index] = (S16) llmath::llround(((fmod(te->mRotation, F_TWO_PI)/F_TWO_PI) * TEXTURE_ROTATION_PACK_FACTOR)); bump[face_index] = te->getBumpShinyFullbright(); media_flags[face_index] = te->getMediaTexGen(); - glow[face_index] = (U8) llround((llclamp(te->getGlow(), 0.0f, 1.0f) * (F32)0xFF)); + glow[face_index] = (U8) llmath::llround((llclamp(te->getGlow(), 0.0f, 1.0f) * (F32)0xFF)); // Directly sending material_ids is not safe! memcpy(&material_data[face_index*16],getTE(face_index)->getMaterialID().get(),16); /* Flawfinder: ignore */ diff --git a/indra/llprimitive/llvolumemessage.cpp b/indra/llprimitive/llvolumemessage.cpp index 273ed1b7e..53f035fc3 100644 --- a/indra/llprimitive/llvolumemessage.cpp +++ b/indra/llprimitive/llvolumemessage.cpp @@ -58,13 +58,13 @@ bool LLVolumeMessage::packProfileParams( tempU8 = params->getCurveType(); mesgsys->addU8Fast(_PREHASH_ProfileCurve, tempU8); - tempU16 = (U16) llround( params->getBegin() / CUT_QUANTA); + tempU16 = (U16) llmath::llround( params->getBegin() / CUT_QUANTA); mesgsys->addU16Fast(_PREHASH_ProfileBegin, tempU16); - tempU16 = 50000 - (U16) llround(params->getEnd() / CUT_QUANTA); + tempU16 = 50000 - (U16) llmath::llround(params->getEnd() / CUT_QUANTA); mesgsys->addU16Fast(_PREHASH_ProfileEnd, tempU16); - tempU16 = (U16) llround(params->getHollow() / HOLLOW_QUANTA); + tempU16 = (U16) llmath::llround(params->getHollow() / HOLLOW_QUANTA); mesgsys->addU16Fast(_PREHASH_ProfileHollow, tempU16); return true; @@ -86,13 +86,13 @@ bool LLVolumeMessage::packProfileParams( tempU8 = params->getCurveType(); dp.packU8(tempU8, "Curve"); - tempU16 = (U16) llround( params->getBegin() / CUT_QUANTA); + tempU16 = (U16) llmath::llround( params->getBegin() / CUT_QUANTA); dp.packU16(tempU16, "Begin"); - tempU16 = 50000 - (U16) llround(params->getEnd() / CUT_QUANTA); + tempU16 = 50000 - (U16) llmath::llround(params->getEnd() / CUT_QUANTA); dp.packU16(tempU16, "End"); - tempU16 = (U16) llround(params->getHollow() / HOLLOW_QUANTA); + tempU16 = (U16) llmath::llround(params->getHollow() / HOLLOW_QUANTA); dp.packU16(tempU16, "Hollow"); return true; } @@ -223,46 +223,46 @@ bool LLVolumeMessage::packPathParams( U8 curve = params->getCurveType(); mesgsys->addU8Fast(_PREHASH_PathCurve, curve); - U16 begin = (U16) llround(params->getBegin() / CUT_QUANTA); + U16 begin = (U16) llmath::llround(params->getBegin() / CUT_QUANTA); mesgsys->addU16Fast(_PREHASH_PathBegin, begin); - U16 end = 50000 - (U16) llround(params->getEnd() / CUT_QUANTA); + U16 end = 50000 - (U16) llmath::llround(params->getEnd() / CUT_QUANTA); mesgsys->addU16Fast(_PREHASH_PathEnd, end); // Avoid truncation problem with direct F32->U8 cast. // (e.g., (U8) (0.50 / 0.01) = (U8) 49.9999999 = 49 not 50. - U8 pack_scale_x = 200 - (U8) llround(params->getScaleX() / SCALE_QUANTA); + U8 pack_scale_x = 200 - (U8) llmath::llround(params->getScaleX() / SCALE_QUANTA); mesgsys->addU8Fast(_PREHASH_PathScaleX, pack_scale_x ); - U8 pack_scale_y = 200 - (U8) llround(params->getScaleY() / SCALE_QUANTA); + U8 pack_scale_y = 200 - (U8) llmath::llround(params->getScaleY() / SCALE_QUANTA); mesgsys->addU8Fast(_PREHASH_PathScaleY, pack_scale_y ); - U8 pack_shear_x = (U8) llround(params->getShearX() / SHEAR_QUANTA); + U8 pack_shear_x = (U8) llmath::llround(params->getShearX() / SHEAR_QUANTA); mesgsys->addU8Fast(_PREHASH_PathShearX, pack_shear_x ); - U8 pack_shear_y = (U8) llround(params->getShearY() / SHEAR_QUANTA); + U8 pack_shear_y = (U8) llmath::llround(params->getShearY() / SHEAR_QUANTA); mesgsys->addU8Fast(_PREHASH_PathShearY, pack_shear_y ); - S8 twist = (S8) llround(params->getTwist() / SCALE_QUANTA); + S8 twist = (S8) llmath::llround(params->getTwist() / SCALE_QUANTA); mesgsys->addS8Fast(_PREHASH_PathTwist, twist); - S8 twist_begin = (S8) llround(params->getTwistBegin() / SCALE_QUANTA); + S8 twist_begin = (S8) llmath::llround(params->getTwistBegin() / SCALE_QUANTA); mesgsys->addS8Fast(_PREHASH_PathTwistBegin, twist_begin); - S8 radius_offset = (S8) llround(params->getRadiusOffset() / SCALE_QUANTA); + S8 radius_offset = (S8) llmath::llround(params->getRadiusOffset() / SCALE_QUANTA); mesgsys->addS8Fast(_PREHASH_PathRadiusOffset, radius_offset); - S8 taper_x = (S8) llround(params->getTaperX() / TAPER_QUANTA); + S8 taper_x = (S8) llmath::llround(params->getTaperX() / TAPER_QUANTA); mesgsys->addS8Fast(_PREHASH_PathTaperX, taper_x); - S8 taper_y = (S8) llround(params->getTaperY() / TAPER_QUANTA); + S8 taper_y = (S8) llmath::llround(params->getTaperY() / TAPER_QUANTA); mesgsys->addS8Fast(_PREHASH_PathTaperY, taper_y); - U8 revolutions = (U8) llround( (params->getRevolutions() - 1.0f) / REV_QUANTA); + U8 revolutions = (U8) llmath::llround( (params->getRevolutions() - 1.0f) / REV_QUANTA); mesgsys->addU8Fast(_PREHASH_PathRevolutions, revolutions); - S8 skew = (S8) llround(params->getSkew() / SCALE_QUANTA); + S8 skew = (S8) llmath::llround(params->getSkew() / SCALE_QUANTA); mesgsys->addS8Fast(_PREHASH_PathSkew, skew); return true; @@ -280,46 +280,46 @@ bool LLVolumeMessage::packPathParams( U8 curve = params->getCurveType(); dp.packU8(curve, "Curve"); - U16 begin = (U16) llround(params->getBegin() / CUT_QUANTA); + U16 begin = (U16) llmath::llround(params->getBegin() / CUT_QUANTA); dp.packU16(begin, "Begin"); - U16 end = 50000 - (U16) llround(params->getEnd() / CUT_QUANTA); + U16 end = 50000 - (U16) llmath::llround(params->getEnd() / CUT_QUANTA); dp.packU16(end, "End"); // Avoid truncation problem with direct F32->U8 cast. // (e.g., (U8) (0.50 / 0.01) = (U8) 49.9999999 = 49 not 50. - U8 pack_scale_x = 200 - (U8) llround(params->getScaleX() / SCALE_QUANTA); + U8 pack_scale_x = 200 - (U8) llmath::llround(params->getScaleX() / SCALE_QUANTA); dp.packU8(pack_scale_x, "ScaleX"); - U8 pack_scale_y = 200 - (U8) llround(params->getScaleY() / SCALE_QUANTA); + U8 pack_scale_y = 200 - (U8) llmath::llround(params->getScaleY() / SCALE_QUANTA); dp.packU8(pack_scale_y, "ScaleY"); - S8 pack_shear_x = (S8) llround(params->getShearX() / SHEAR_QUANTA); + S8 pack_shear_x = (S8) llmath::llround(params->getShearX() / SHEAR_QUANTA); dp.packU8(*(U8 *)&pack_shear_x, "ShearX"); - S8 pack_shear_y = (S8) llround(params->getShearY() / SHEAR_QUANTA); + S8 pack_shear_y = (S8) llmath::llround(params->getShearY() / SHEAR_QUANTA); dp.packU8(*(U8 *)&pack_shear_y, "ShearY"); - S8 twist = (S8) llround(params->getTwist() / SCALE_QUANTA); + S8 twist = (S8) llmath::llround(params->getTwist() / SCALE_QUANTA); dp.packU8(*(U8 *)&twist, "Twist"); - S8 twist_begin = (S8) llround(params->getTwistBegin() / SCALE_QUANTA); + S8 twist_begin = (S8) llmath::llround(params->getTwistBegin() / SCALE_QUANTA); dp.packU8(*(U8 *)&twist_begin, "TwistBegin"); - S8 radius_offset = (S8) llround(params->getRadiusOffset() / SCALE_QUANTA); + S8 radius_offset = (S8) llmath::llround(params->getRadiusOffset() / SCALE_QUANTA); dp.packU8(*(U8 *)&radius_offset, "RadiusOffset"); - S8 taper_x = (S8) llround(params->getTaperX() / TAPER_QUANTA); + S8 taper_x = (S8) llmath::llround(params->getTaperX() / TAPER_QUANTA); dp.packU8(*(U8 *)&taper_x, "TaperX"); - S8 taper_y = (S8) llround(params->getTaperY() / TAPER_QUANTA); + S8 taper_y = (S8) llmath::llround(params->getTaperY() / TAPER_QUANTA); dp.packU8(*(U8 *)&taper_y, "TaperY"); - U8 revolutions = (U8) llround( (params->getRevolutions() - 1.0f) / REV_QUANTA); + U8 revolutions = (U8) llmath::llround( (params->getRevolutions() - 1.0f) / REV_QUANTA); dp.packU8(*(U8 *)&revolutions, "Revolutions"); - S8 skew = (S8) llround(params->getSkew() / SCALE_QUANTA); + S8 skew = (S8) llmath::llround(params->getSkew() / SCALE_QUANTA); dp.packU8(*(U8 *)&skew, "Skew"); return true; diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp index 30b0ed429..f2839bb67 100644 --- a/indra/llrender/llfontfreetype.cpp +++ b/indra/llrender/llfontfreetype.cpp @@ -184,8 +184,8 @@ BOOL LLFontFreetype::loadFace(const std::string& filename, const F32 point_size, mDescender = -mFTFace->descender * pixels_per_unit; mLineHeight = mFTFace->height * pixels_per_unit; - S32 max_char_width = llround(0.5f + (x_max - x_min)); - S32 max_char_height = llround(0.5f + (y_max - y_min)); + S32 max_char_width = llmath::llround(0.5f + (x_max - x_min)); + S32 max_char_height = llmath::llround(0.5f + (y_max - y_min)); mFontBitmapCachep->init(components, max_char_width, max_char_height); diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 31d48f2ab..4c03726b7 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -230,10 +230,10 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons case LEFT: break; case RIGHT: - cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)); + cur_x -= llmin(scaled_max_pixels, llmath::llround(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)); break; case HCENTER: - cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)) / 2; + cur_x -= llmin(scaled_max_pixels, llmath::llround(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)) / 2; break; default: break; @@ -242,7 +242,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons cur_render_y = cur_y; cur_render_x = cur_x; - F32 start_x = (F32)llround(cur_x); + F32 start_x = (F32)llmath::llround(cur_x); const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache(); @@ -256,12 +256,12 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons if (use_ellipses && halign == LEFT) { // check for too long of a string - S32 string_width = llround(getWidthF32(wstr.c_str(), begin_offset, max_chars) * sScaleX); + S32 string_width = llmath::llround(getWidthF32(wstr.c_str(), begin_offset, max_chars) * sScaleX); if (string_width > scaled_max_pixels) { // use four dots for ellipsis width to generate padding const LLWString dots(utf8str_to_wstring(std::string("...."))); - scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str()))); + scaled_max_pixels = llmax(0, scaled_max_pixels - llmath::llround(getWidthF32(dots.c_str()))); draw_ellipses = TRUE; } } @@ -308,8 +308,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons gGL.getTexUnit(0)->bind(ext_image); // snap origin to whole screen pixel - const F32 ext_x = (F32)llround(cur_render_x + (EXT_X_BEARING * sScaleX)); - const F32 ext_y = (F32)llround(cur_render_y + (EXT_Y_BEARING * sScaleY + mFontFreetype->getAscenderHeight() - mFontFreetype->getLineHeight())); + const F32 ext_x = (F32)llmath::llround(cur_render_x + (EXT_X_BEARING * sScaleX)); + const F32 ext_y = (F32)llmath::llround(cur_render_y + (EXT_Y_BEARING * sScaleY + mFontFreetype->getAscenderHeight() - mFontFreetype->getLineHeight())); LLRectf uv_rect(0.f, 1.f, 1.f, 0.f); LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y); @@ -398,10 +398,10 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons (fgi->mXBitmapOffset + fgi->mWidth) * inv_width, (fgi->mYBitmapOffset - PAD_UVY) * inv_height); // snap glyph origin to whole screen pixel - LLRectf screen_rect((F32)llround(cur_render_x + (F32)fgi->mXBearing), - (F32)llround(cur_render_y + (F32)fgi->mYBearing), - (F32)llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth, - (F32)llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight); + LLRectf screen_rect((F32)llmath::llround(cur_render_x + (F32)fgi->mXBearing), + (F32)llmath::llround(cur_render_y + (F32)fgi->mYBearing), + (F32)llmath::llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth, + (F32)llmath::llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight); if (glyph_count >= GLYPH_BATCH_SIZE) { @@ -432,8 +432,8 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons // Must do this to cur_x, not just to cur_render_x, otherwise you // will squish sub-pixel kerned characters too close together. // For example, "CCCCC" looks bad. - cur_x = (F32)llround(cur_x); - //cur_y = (F32)llround(cur_y); + cur_x = (F32)llmath::llround(cur_x); + //cur_y = (F32)llmath::llround(cur_y); cur_render_x = cur_x; cur_render_y = cur_y; @@ -523,7 +523,7 @@ F32 LLFontGL::getDescenderHeight() const F32 LLFontGL::getLineHeight() const { - return (F32)llround(mFontFreetype->getLineHeight() / sScaleY); + return (F32)llmath::llround(mFontFreetype->getLineHeight() / sScaleY); } S32 LLFontGL::getWidth(const std::string& utf8text) const @@ -546,7 +546,7 @@ S32 LLFontGL::getWidth(const std::string& utf8text, const S32 begin_offset, cons S32 LLFontGL::getWidth(const llwchar* wchars, const S32 begin_offset, const S32 max_chars, BOOL use_embedded) const { F32 width = getWidthF32(wchars, begin_offset, max_chars, use_embedded); - return llround(width); + return llmath::llround(width); } F32 LLFontGL::getWidthF32(const std::string& utf8text) const @@ -620,7 +620,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, const S32 begin_offset, const S cur_x += mFontFreetype->getXKerning(fgi, next_glyph); } // Round after kerning. - cur_x = (F32)llround(cur_x); + cur_x = (F32)llmath::llround(cur_x); } } @@ -749,7 +749,7 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch } } // Round after kerning. - cur_x = (F32)llround(cur_x); + cur_x = (F32)llmath::llround(cur_x); drawn_x = cur_x; } @@ -837,7 +837,7 @@ S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_ } // Round after kerning. - total_width = (F32)llround(total_width); + total_width = (F32)llmath::llround(total_width); } if (drawable_chars == 0) @@ -929,7 +929,7 @@ S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, const S32 begin_offset, // Round after kerning. - cur_x = (F32)llround(cur_x); + cur_x = (F32)llmath::llround(cur_x); } diff --git a/indra/llrender/llpostprocess.cpp b/indra/llrender/llpostprocess.cpp index 18be01d24..4a2ad8302 100644 --- a/indra/llrender/llpostprocess.cpp +++ b/indra/llrender/llpostprocess.cpp @@ -637,8 +637,8 @@ void LLPostProcess::drawOrthoQuad(QuadType type) mVBO->getTexCoord1Strider(uv2); float offs[2] = { - llround(((float) rand() / (float) RAND_MAX) * (float)NOISE_SIZE)/float(NOISE_SIZE), - llround(((float) rand() / (float) RAND_MAX) * (float)NOISE_SIZE)/float(NOISE_SIZE) }; + llmath::llround(((float) rand() / (float) RAND_MAX) * (float)NOISE_SIZE)/float(NOISE_SIZE), + llmath::llround(((float) rand() / (float) RAND_MAX) * (float)NOISE_SIZE)/float(NOISE_SIZE) }; float scale[2] = { (float)mScreenWidth * mNoiseTextureScale, (float)mScreenHeight * mNoiseTextureScale }; diff --git a/indra/llrender/llrender2dutils.cpp b/indra/llrender/llrender2dutils.cpp index 7ab223f4d..f8b54ee68 100644 --- a/indra/llrender/llrender2dutils.cpp +++ b/indra/llrender/llrender2dutils.cpp @@ -401,8 +401,8 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLTex F32 image_width = image->getWidth(0); F32 image_height = image->getHeight(0); - S32 image_natural_width = llround(image_width * uv_width); - S32 image_natural_height = llround(image_height * uv_height); + S32 image_natural_width = llmath::llround(image_width * uv_width); + S32 image_natural_height = llmath::llround(image_height * uv_height); LLRectf draw_center_rect( uv_center_rect.mLeft * image_width, uv_center_rect.mTop * image_height, @@ -421,10 +421,10 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLTex F32 shrink_scale = 1.f - llmax(shrink_width_ratio, shrink_height_ratio); - draw_center_rect.mLeft = llround(ui_translation.mV[VX] + (F32)draw_center_rect.mLeft * shrink_scale * ui_scale.mV[VX]); - draw_center_rect.mTop = llround(ui_translation.mV[VY] + lerp((F32)height, (F32)draw_center_rect.mTop, shrink_scale) * ui_scale.mV[VY]); - draw_center_rect.mRight = llround(ui_translation.mV[VX] + lerp((F32)width, (F32)draw_center_rect.mRight, shrink_scale) * ui_scale.mV[VX]); - draw_center_rect.mBottom = llround(ui_translation.mV[VY] + (F32)draw_center_rect.mBottom * shrink_scale * ui_scale.mV[VY]); + draw_center_rect.mLeft = llmath::llround(ui_translation.mV[VX] + (F32)draw_center_rect.mLeft * shrink_scale * ui_scale.mV[VX]); + draw_center_rect.mTop = llmath::llround(ui_translation.mV[VY] + lerp((F32)height, (F32)draw_center_rect.mTop, shrink_scale) * ui_scale.mV[VY]); + draw_center_rect.mRight = llmath::llround(ui_translation.mV[VX] + lerp((F32)width, (F32)draw_center_rect.mRight, shrink_scale) * ui_scale.mV[VX]); + draw_center_rect.mBottom = llmath::llround(ui_translation.mV[VY] + (F32)draw_center_rect.mBottom * shrink_scale * ui_scale.mV[VY]); } LLRectf draw_outer_rect(ui_translation.mV[VX], @@ -664,8 +664,8 @@ void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degre ui_translation.mV[VY] += y; ui_translation.scaleVec(ui_scale); S32 index = 0; - S32 scaled_width = llround(width * ui_scale.mV[VX]); - S32 scaled_height = llround(height * ui_scale.mV[VY]); + S32 scaled_width = llmath::llround(width * ui_scale.mV[VX]); + S32 scaled_height = llmath::llround(height * ui_scale.mV[VY]); uv[index] = LLVector2(uv_rect.mRight, uv_rect.mTop); pos[index].set(ui_translation.mV[VX] + scaled_width, ui_translation.mV[VY] + scaled_height, 0.f); diff --git a/indra/llrender/lluiimage.cpp b/indra/llrender/lluiimage.cpp index da122425c..7aa750756 100644 --- a/indra/llrender/lluiimage.cpp +++ b/indra/llrender/lluiimage.cpp @@ -115,13 +115,13 @@ void LLUIImage::drawBorder(S32 x, S32 y, S32 width, S32 height, const LLColor4& S32 LLUIImage::getWidth() const { // return clipped dimensions of actual image area - return llround((F32)mImage->getWidth(0) * mClipRegion.getWidth()); + return llmath::llround((F32)mImage->getWidth(0) * mClipRegion.getWidth()); } S32 LLUIImage::getHeight() const { // return clipped dimensions of actual image area - return llround((F32)mImage->getHeight(0) * mClipRegion.getHeight()); + return llmath::llround((F32)mImage->getHeight(0) * mClipRegion.getHeight()); } S32 LLUIImage::getTextureWidth() const diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 1b9add0a4..ed318c473 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -536,8 +536,8 @@ void LLButton::getOverlayImageSize(S32& overlay_width, S32& overlay_height) overlay_height = mImageOverlay->getHeight(); F32 scale_factor = llmin((F32)getRect().getWidth() / (F32)overlay_width, (F32)getRect().getHeight() / (F32)overlay_height, 1.f); - overlay_width = llround((F32)overlay_width * scale_factor); - overlay_height = llround((F32)overlay_height * scale_factor); + overlay_width = llmath::llround((F32)overlay_width * scale_factor); + overlay_height = llmath::llround((F32)overlay_height * scale_factor); } @@ -696,7 +696,7 @@ void LLButton::draw() if (hasFocus()) { F32 lerp_amt = gFocusMgr.getFocusFlashAmt(); - drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, llround(lerp(1.f, 3.f, lerp_amt))); + drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, llmath::llround(lerp(1.f, 3.f, lerp_amt))); } if (use_glow_effect) @@ -981,7 +981,7 @@ void LLButton::resize(LLUIString label) { S32 overlay_width = mImageOverlay->getWidth(); F32 scale_factor = (getRect().getHeight() - (mImageOverlayBottomPad + mImageOverlayTopPad)) / (F32)mImageOverlay->getHeight(); - overlay_width = llround((F32)overlay_width * scale_factor); + overlay_width = llmath::llround((F32)overlay_width * scale_factor); switch(mImageOverlayAlignment) { diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index ada81b639..caf81cd14 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -83,7 +83,7 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const std::string& name, const LLRect& rect, // Label (add a little space to make sure text actually renders) const S32 FUDGE = 10; S32 text_width = mFont->getWidth( label ) + FUDGE; - S32 text_height = llround(mFont->getLineHeight()); + S32 text_height = llmath::llround(mFont->getLineHeight()); LLRect label_rect; label_rect.setOriginAndSize( LLCHECKBOXCTRL_HPAD + LLCHECKBOXCTRL_BTN_SIZE + LLCHECKBOXCTRL_SPACING, @@ -187,7 +187,7 @@ void LLCheckBoxCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) //stretch or shrink bounding rectangle of label when rebuilding UI at new scale const S32 FUDGE = 10; S32 text_width = mFont->getWidth( mLabel->getText() ) + FUDGE; - S32 text_height = llround(mFont->getLineHeight()); + S32 text_height = llmath::llround(mFont->getLineHeight()); LLRect label_rect; label_rect.setOriginAndSize( LLCHECKBOXCTRL_HPAD + LLCHECKBOXCTRL_BTN_SIZE + LLCHECKBOXCTRL_SPACING, diff --git a/indra/llui/lldraghandle.cpp b/indra/llui/lldraghandle.cpp index 33f1a1a1c..de5cbbe7a 100644 --- a/indra/llui/lldraghandle.cpp +++ b/indra/llui/lldraghandle.cpp @@ -247,7 +247,7 @@ void LLDragHandleTop::reshapeTitleBox() S32 title_width = font->getWidth( getTitleBox()->getText() ) + TITLE_PAD; if (getMaxTitleWidth() > 0) title_width = llmin(title_width, getMaxTitleWidth()); - S32 title_height = llround(font->getLineHeight()); + S32 title_height = llmath::llround(font->getLineHeight()); LLRect title_rect; title_rect.setLeftTopAndSize( LEFT_PAD, diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index c8be07f43..2fa117424 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -1449,7 +1449,7 @@ void LLFloater::draw() } gl_drop_shadow(left, top, right, bottom, shadow_color, - llround(shadow_offset)); + llmath::llround(shadow_offset)); // No transparent windows in simple UI if (isBackgroundOpaque()) @@ -1629,16 +1629,16 @@ void LLFloater::updateButtons() btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH, getRect().getHeight() - CLOSE_BOX_FROM_TOP - (LLFLOATER_CLOSE_BOX_SIZE + 1) * button_count, - llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale), - llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale)); + llmath::llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale), + llmath::llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale)); } else { btn_rect.setLeftTopAndSize( getRect().getWidth() - LLPANEL_BORDER_WIDTH - (LLFLOATER_CLOSE_BOX_SIZE + 1) * button_count, getRect().getHeight() - CLOSE_BOX_FROM_TOP, - llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale), - llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale)); + llmath::llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale), + llmath::llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale)); } mButtons[i]->setRect(btn_rect); @@ -1665,16 +1665,16 @@ void LLFloater::buildButtons() btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH, getRect().getHeight() - CLOSE_BOX_FROM_TOP - (LLFLOATER_CLOSE_BOX_SIZE + 1) * (i + 1), - llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale), - llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale)); + llmath::llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale), + llmath::llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale)); } else { btn_rect.setLeftTopAndSize( getRect().getWidth() - LLPANEL_BORDER_WIDTH - (LLFLOATER_CLOSE_BOX_SIZE + 1) * (i + 1), getRect().getHeight() - CLOSE_BOX_FROM_TOP, - llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale), - llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale)); + llmath::llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale), + llmath::llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale)); } LLButton* buttonp = new LLButton( diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h index 7e5e50dfb..fd09bf55f 100644 --- a/indra/llui/llfocusmgr.h +++ b/indra/llui/llfocusmgr.h @@ -94,7 +94,7 @@ public: F32 getFocusTime() const { return mFocusFlashTimer.getElapsedTimeF32(); } F32 getFocusFlashAmt() const; - S32 getFocusFlashWidth() const { return llround(lerp(1.f, 3.f, getFocusFlashAmt())); } + S32 getFocusFlashWidth() const { return llmath::llround(lerp(1.f, 3.f, getFocusFlashAmt())); } LLColor4 getFocusColor() const; void triggerFocusFlash(); BOOL getAppHasFocus() const { return mAppHasFocus; } diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 83e532a65..73d428f64 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -122,7 +122,7 @@ F32 LLLayoutPanel::getVisibleAmount() const S32 LLLayoutPanel::getLayoutDim() const { - return llround((F32)((mOrientation == LLLayoutStack::HORIZONTAL) + return llmath::llround((F32)((mOrientation == LLLayoutStack::HORIZONTAL) ? getRect().getWidth() : getRect().getHeight())); } @@ -149,7 +149,7 @@ void LLLayoutPanel::setTargetDim(S32 value) S32 LLLayoutPanel::getVisibleDim() const { F32 min_dim = getRelevantMinDim(); - return llround(mVisibleAmt + return llmath::llround(mVisibleAmt * (min_dim + (((F32)mTargetDim - min_dim) * (1.f - mCollapseAmt)))); } @@ -157,7 +157,7 @@ S32 LLLayoutPanel::getVisibleDim() const void LLLayoutPanel::setOrientation( LLLayoutStack::ELayoutOrientation orientation ) { mOrientation = orientation; - S32 layout_dim = llround((F32)((mOrientation == LLLayoutStack::HORIZONTAL) + S32 layout_dim = llmath::llround((F32)((mOrientation == LLLayoutStack::HORIZONTAL) ? getRect().getWidth() : getRect().getHeight())); @@ -374,14 +374,14 @@ void LLLayoutStack::updateLayout() { panelp->mTargetDim = panelp->getRelevantMinDim(); } - space_to_distribute -= panelp->getVisibleDim() + llround((F32)mPanelSpacing * panelp->getVisibleAmount()); + space_to_distribute -= panelp->getVisibleDim() + llmath::llround((F32)mPanelSpacing * panelp->getVisibleAmount()); total_visible_fraction += panelp->mFractionalSize * panelp->getAutoResizeFactor(); } llassert(total_visible_fraction < 1.05f); // don't need spacing after last panel - space_to_distribute += panelp ? llround((F32)mPanelSpacing * panelp->getVisibleAmount()) : 0; + space_to_distribute += panelp ? llmath::llround((F32)mPanelSpacing * panelp->getVisibleAmount()) : 0; S32 remaining_space = space_to_distribute; F32 fraction_distributed = 0.f; @@ -392,7 +392,7 @@ void LLLayoutStack::updateLayout() if (panelp->mAutoResize) { F32 fraction_to_distribute = (panelp->mFractionalSize * panelp->getAutoResizeFactor()) / (total_visible_fraction); - S32 delta = llround((F32)space_to_distribute * fraction_to_distribute); + S32 delta = llmath::llround((F32)space_to_distribute * fraction_to_distribute); fraction_distributed += fraction_to_distribute; panelp->mTargetDim += delta; remaining_space -= delta; @@ -425,17 +425,17 @@ void LLLayoutStack::updateLayout() LLRect panel_rect; if (mOrientation == HORIZONTAL) { - panel_rect.setLeftTopAndSize(llround(cur_pos), + panel_rect.setLeftTopAndSize(llmath::llround(cur_pos), getRect().getHeight(), - llround(panel_dim), + llmath::llround(panel_dim), getRect().getHeight()); } else { panel_rect.setLeftTopAndSize(0, - llround(cur_pos), + llmath::llround(cur_pos), getRect().getWidth(), - llround(panel_dim)); + llmath::llround(panel_dim)); } panelp->setIgnoreReshape(true); panelp->setShape(panel_rect); @@ -447,14 +447,14 @@ void LLLayoutStack::updateLayout() if (mOrientation == HORIZONTAL) { resize_bar_rect.mLeft = panel_rect.mRight - mResizeBarOverlap; - resize_bar_rect.mRight = panel_rect.mRight + (S32)(llround(panel_spacing)) + mResizeBarOverlap; + resize_bar_rect.mRight = panel_rect.mRight + (S32)(llmath::llround(panel_spacing)) + mResizeBarOverlap; cur_pos += panel_visible_dim + panel_spacing; } else //VERTICAL { resize_bar_rect.mTop = panel_rect.mBottom + mResizeBarOverlap; - resize_bar_rect.mBottom = panel_rect.mBottom - (S32)(llround(panel_spacing)) - mResizeBarOverlap; + resize_bar_rect.mBottom = panel_rect.mBottom - (S32)(llmath::llround(panel_spacing)) - mResizeBarOverlap; cur_pos -= panel_visible_dim + panel_spacing; } diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 6276c0f50..311003b6b 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -512,7 +512,7 @@ std::vector LLLineEditor::getMisspelledWordsPositions() { //misspelled word here, and you have just right clicked on it! //get the center of this word.. - //S32 center = llround( (wordEnd-wordStart)/2 ) + wordStart; + //S32 center = llmath::llround( (wordEnd-wordStart)/2 ) + wordStart; //turn this cursor position into a pixel pos //center = findPixelNearestPos(center-getCursor()); @@ -769,7 +769,7 @@ BOOL LLLineEditor::handleHover(S32 x, S32 y, MASK mask) // Scroll if mouse cursor outside of bounds if (mScrollTimer.hasExpired()) { - S32 increment = llround(mScrollTimer.getElapsedTimeF32() / AUTO_SCROLL_TIME); + S32 increment = llmath::llround(mScrollTimer.getElapsedTimeF32() / AUTO_SCROLL_TIME); mScrollTimer.reset(AUTO_SCROLL_TIME); if( (x < mMinHPixels) && (mScrollHPos > 0 ) ) { @@ -1929,7 +1929,7 @@ void LLLineEditor::draw() LLFontGL::NORMAL, LLFontGL::NO_SHADOW, select_left - mScrollHPos, - mMaxHPixels - llround(rendered_pixels_right), + mMaxHPixels - llmath::llround(rendered_pixels_right), &rendered_pixels_right); } @@ -1938,8 +1938,8 @@ void LLLineEditor::draw() LLColor4 color(1.f - bg_color.mV[0], 1.f - bg_color.mV[1], 1.f - bg_color.mV[2], alpha ); // selected middle S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text); - width = llmin(width, mMaxHPixels - llround(rendered_pixels_right)); - gl_rect_2d(llround(rendered_pixels_right), cursor_top, llround(rendered_pixels_right)+width, cursor_bottom, color); + width = llmin(width, mMaxHPixels - llmath::llround(rendered_pixels_right)); + gl_rect_2d(llmath::llround(rendered_pixels_right), cursor_top, llmath::llround(rendered_pixels_right)+width, cursor_bottom, color); LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ); rendered_text += mGLFont->render( @@ -1950,7 +1950,7 @@ void LLLineEditor::draw() LLFontGL::NORMAL, LLFontGL::NO_SHADOW, select_right - mScrollHPos - rendered_text, - mMaxHPixels - llround(rendered_pixels_right), + mMaxHPixels - llmath::llround(rendered_pixels_right), &rendered_pixels_right); } @@ -1965,7 +1965,7 @@ void LLLineEditor::draw() LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, - mMaxHPixels - llround(rendered_pixels_right), + mMaxHPixels - llmath::llround(rendered_pixels_right), &rendered_pixels_right); } } @@ -1979,7 +1979,7 @@ void LLLineEditor::draw() LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, - mMaxHPixels - llround(rendered_pixels_right), + mMaxHPixels - llmath::llround(rendered_pixels_right), &rendered_pixels_right); } #if 0 // for when we're ready for image art. @@ -2047,7 +2047,7 @@ void LLLineEditor::draw() LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, - mMaxHPixels - llround(rendered_pixels_right), + mMaxHPixels - llmath::llround(rendered_pixels_right), &rendered_pixels_right, FALSE); } @@ -2072,7 +2072,7 @@ void LLLineEditor::draw() LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, - mMaxHPixels - llround(rendered_pixels_right), + mMaxHPixels - llmath::llround(rendered_pixels_right), &rendered_pixels_right, FALSE); } // Draw children (border) @@ -3021,7 +3021,7 @@ void LLLineEditor::markAsPreedit(S32 position, S32 length) S32 LLLineEditor::getPreeditFontSize() const { - return llround(mGLFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); + return llmath::llround(mGLFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); } void LLLineEditor::setReplaceNewlinesWithSpaces(BOOL replace) diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 039c7a789..b35913d6d 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -342,7 +342,7 @@ void LLMenuItemGL::setJumpKey(KEY key) // virtual U32 LLMenuItemGL::getNominalHeight( void ) const { - return llround(mFont->getLineHeight()) + MENU_ITEM_PADDING; + return llmath::llround(mFont->getLineHeight()) + MENU_ITEM_PADDING; } //virtual @@ -1826,7 +1826,7 @@ void LLMenuItemBranchDownGL::draw( void ) std::string::size_type offset = upper_case_label.find(getJumpKey()); if (offset != std::string::npos) { - S32 x_offset = llround((F32)getRect().getWidth() / 2.f - getFont()->getWidthF32(mLabel.getString(), 0, S32_MAX) / 2.f); + S32 x_offset = llmath::llround((F32)getRect().getWidth() / 2.f - getFont()->getWidthF32(mLabel.getString(), 0, S32_MAX) / 2.f); S32 x_begin = x_offset + getFont()->getWidth(mLabel, 0, offset); S32 x_end = x_offset + getFont()->getWidth(mLabel, 0, offset + 1); gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end, LABEL_BOTTOM_PAD_PIXELS); @@ -2660,7 +2660,7 @@ void LLMenuGL::arrange( void ) // *FIX: create the item first and then ask for its dimensions? S32 spillover_item_width = PLAIN_PAD_PIXELS + LLFontGL::getFontSansSerif()->getWidth( std::string("More") ); // *TODO: Translate - S32 spillover_item_height = llround(LLFontGL::getFontSansSerif()->getLineHeight()) + MENU_ITEM_PADDING; + S32 spillover_item_height = llmath::llround(LLFontGL::getFontSansSerif()->getLineHeight()) + MENU_ITEM_PADDING; // Scrolling support item_list_t::iterator first_visible_item_iter; @@ -3588,8 +3588,8 @@ BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask ) LLVector2 mouse_avg_dir((F32)mMouseVelX, (F32)mMouseVelY); mouse_avg_dir.normVec(); F32 interp = 0.5f * (llclamp(mouse_dir * mouse_avg_dir, 0.f, 1.f)); - mMouseVelX = llround(lerp((F32)mouse_delta_x, (F32)mMouseVelX, interp)); - mMouseVelY = llround(lerp((F32)mouse_delta_y, (F32)mMouseVelY, interp)); + mMouseVelX = llmath::llround(lerp((F32)mouse_delta_x, (F32)mMouseVelX, interp)); + mMouseVelY = llmath::llround(lerp((F32)mouse_delta_y, (F32)mMouseVelY, interp)); mLastMouseX = x; mLastMouseY = y; diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp index 8c8f94113..e210d856e 100644 --- a/indra/llui/llmodaldialog.cpp +++ b/indra/llui/llmodaldialog.cpp @@ -292,7 +292,7 @@ void LLModalDialog::draw() void LLModalDialog::centerOnScreen() { LLVector2 window_size = LLUI::getWindowSize(); - centerWithin(LLRect(0, 0, llround(window_size.mV[VX]), llround(window_size.mV[VY]))); + centerWithin(LLRect(0, 0, llmath::llround(window_size.mV[VX]), llmath::llround(window_size.mV[VY]))); } diff --git a/indra/llui/llprogressbar.cpp b/indra/llui/llprogressbar.cpp index 0443c480a..36ff5587e 100644 --- a/indra/llui/llprogressbar.cpp +++ b/indra/llui/llprogressbar.cpp @@ -84,7 +84,7 @@ void LLProgressBar::draw() bar_bg_imagep->draw(getLocalRect(), background_color % alpha); LLRect progress_rect = getLocalRect(); - progress_rect.mRight = llround(getRect().getWidth() * (mPercentDone / 100.f)); + progress_rect.mRight = llmath::llround(getRect().getWidth() * (mPercentDone / 100.f)); bar_fg_imagep->draw(progress_rect, LLColor4::white % alpha); } diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index f7a658ad6..ad1b4ead0 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -299,7 +299,7 @@ bool LLScrollContainer::autoScroll(S32 x, S32 y) // clip rect against root view inner_rect_local.intersectWith(screen_local_extents); - S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32()); + S32 auto_scroll_speed = llmath::llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32()); // autoscroll region should take up no more than one third of visible scroller area S32 auto_scroll_region_width = llmin(inner_rect_local.getWidth() / 3, 10); S32 auto_scroll_region_height = llmin(inner_rect_local.getHeight() / 3, 10); diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp index dba472842..cc5453445 100644 --- a/indra/llui/llscrolllistcell.cpp +++ b/indra/llui/llscrolllistcell.cpp @@ -254,7 +254,7 @@ BOOL LLScrollListText::getVisible() const //virtual S32 LLScrollListText::getHeight() const { - return llround(mFont->getLineHeight()); + return llmath::llround(mFont->getLineHeight()); } @@ -326,7 +326,7 @@ void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_col break; } LLRect highlight_rect(left - 2, - llround(mFont->getLineHeight()) + 1, + llmath::llround(mFont->getLineHeight()) + 1, left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1, 1); mRoundedRectImage->draw(highlight_rect, highlight_color); diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 3e6e70703..22bf27f4d 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -602,7 +602,7 @@ bool LLScrollListCtrl::updateColumnWidths() S32 new_width = column->getWidth(); if (column->mRelWidth >= 0) { - new_width = (S32)llround(column->mRelWidth*mItemListRect.getWidth()); + new_width = (S32)llmath::llround(column->mRelWidth*mItemListRect.getWidth()); } else if (column->mDynamicWidth) { @@ -2829,7 +2829,7 @@ void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params } if (new_column->mRelWidth >= 0) { - new_column->setWidth((S32)llround(new_column->mRelWidth*mItemListRect.getWidth())); + new_column->setWidth((S32)llmath::llround(new_column->mRelWidth*mItemListRect.getWidth())); } else if(new_column->mDynamicWidth) { diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index 489f71282..5944d0c11 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -147,7 +147,7 @@ F32 clamp_precision(F32 value, S32 decimal_precision) for (S32 i = 0; i < decimal_precision; i++) clamped_value *= 10.0; - clamped_value = llround((F32)clamped_value); + clamped_value = llmath::llround((F32)clamped_value); for (S32 i = 0; i < decimal_precision; i++) clamped_value /= 10.0; @@ -161,13 +161,13 @@ F32 get_increment(F32 inc, S32 decimal_precision) //CF: finetune increments if(gKeyboard->getKeyDown(KEY_ALT)) inc = inc * 10.f; else if(gKeyboard->getKeyDown(KEY_CONTROL)) { - if (llround(inc * 1000.f) == 25) // 0.025 gets 0.05 here + if (llmath::llround(inc * 1000.f) == 25) // 0.025 gets 0.05 here inc = inc * 0.2f; else inc = inc * 0.1f; } else if(gKeyboard->getKeyDown(KEY_SHIFT)) { - if (decimal_precision == 2 && llround(inc) == 1) // for rotations, finest step is 0.05 + if (decimal_precision == 2 && llmath::llround(inc) == 1) // for rotations, finest step is 0.05 inc = inc * 0.05f; else inc = inc * 0.01f; diff --git a/indra/llui/llstatgraph.cpp b/indra/llui/llstatgraph.cpp index 2e3be1803..c271e1706 100644 --- a/indra/llui/llstatgraph.cpp +++ b/indra/llui/llstatgraph.cpp @@ -120,7 +120,7 @@ void LLStatGraph::draw() color = mThresholdColors[i]; gGL.color4fv(color.mV); - gl_rect_2d(1, llround(frac*getRect().getHeight()), getRect().getWidth() - 1, 0, TRUE); + gl_rect_2d(1, llmath::llround(frac*getRect().getHeight()), getRect().getWidth() - 1, 0, TRUE); } void LLStatGraph::setValue(const LLSD& value) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 08989e628..4c210cb14 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -312,7 +312,7 @@ LLTextEditor::LLTextEditor( updateTextRect(); - S32 line_height = llround( mGLFont->getLineHeight() ); + S32 line_height = llmath::llround( mGLFont->getLineHeight() ); S32 page_size = mTextRect.getHeight() / line_height; // Init the scrollbar @@ -970,7 +970,7 @@ S32 LLTextEditor::getCursorPosFromLocalCoord( S32 local_x, S32 local_y, BOOL rou // Figure out which line we're nearest to. S32 total_lines = getLineCount(); - S32 line_height = llround( mGLFont->getLineHeight() ); + S32 line_height = llmath::llround( mGLFont->getLineHeight() ); S32 max_visible_lines = mTextRect.getHeight() / line_height; S32 scroll_lines = mScrollbar->getDocPos(); S32 visible_lines = llmin( total_lines - scroll_lines, max_visible_lines ); // Lines currently visible @@ -2927,7 +2927,7 @@ void LLTextEditor::drawSelectionBackground() const S32 text_len = getLength(); std::queue line_endings; - S32 line_height = llround( mGLFont->getLineHeight() ); + S32 line_height = llmath::llround( mGLFont->getLineHeight() ); S32 selection_left = llmin( mSelectionStart, mSelectionEnd ); S32 selection_right = llmax( mSelectionStart, mSelectionEnd ); @@ -3122,7 +3122,7 @@ void LLTextEditor::drawMisspelled() S32 line_end = 0; // Determine if the cursor is visible and if so what its coordinates are. - while( (mTextRect.mBottom <= llround(text_y)) && (search_pos < num_lines)) + while( (mTextRect.mBottom <= llmath::llround(text_y)) && (search_pos < num_lines)) { line_end = text_len + 1; S32 next_line = -1; @@ -3197,7 +3197,7 @@ void LLTextEditor::drawCursor() S32 line_end = 0; // Determine if the cursor is visible and if so what its coordinates are. - while( (mTextRect.mBottom <= llround(text_y)) && (cur_pos < num_lines)) + while( (mTextRect.mBottom <= llmath::llround(text_y)) && (cur_pos < num_lines)) { line_end = text_len + 1; S32 next_line = -1; @@ -3316,7 +3316,7 @@ void LLTextEditor::drawPreeditMarker() return; } - const S32 line_height = llround( mGLFont->getLineHeight() ); + const S32 line_height = llmath::llround( mGLFont->getLineHeight() ); S32 line_start = getLineStart(cur_line); S32 line_y = mTextRect.mTop - line_height; @@ -3436,7 +3436,7 @@ void LLTextEditor::drawText() if (seg_iter == mSegments.end() || (*seg_iter)->getStart() > line_start) --seg_iter; LLTextSegment* cur_segment = *seg_iter; - S32 line_height = llround( mGLFont->getLineHeight() ); + S32 line_height = llmath::llround( mGLFont->getLineHeight() ); F32 text_y = (F32)(mTextRect.mTop - line_height); while((mTextRect.mBottom <= text_y) && (cur_line < num_lines)) { @@ -3461,7 +3461,7 @@ void LLTextEditor::drawText() if( mShowLineNumbers && !cur_line_is_continuation) { const LLFontGL *num_font = LLFontGL::getFontMonospace(); - F32 y_top = text_y + ((F32)llround(num_font->getLineHeight()) / 2); + F32 y_top = text_y + ((F32)llmath::llround(num_font->getLineHeight()) / 2); const LLWString ltext = utf8str_to_wstring(llformat("%*d", UI_TEXTEDITOR_LINE_NUMBER_DIGITS, cur_line_num )); BOOL is_cur_line = getCurrentLine() == cur_line_num; const U8 style = is_cur_line ? LLFontGL::BOLD : LLFontGL::NORMAL; @@ -3505,7 +3505,7 @@ void LLTextEditor::drawText() S32 style_image_height = style->mImageHeight; S32 style_image_width = style->mImageWidth; LLUIImagePtr image = style->getImage(); - image->draw(llround(text_x), llround(text_y)+line_height-style_image_height, + image->draw(llmath::llround(text_x), llmath::llround(text_y)+line_height-style_image_height, style_image_width, style_image_height); } @@ -3592,7 +3592,7 @@ void LLTextEditor::drawClippedSegment(const LLWString &text, S32 seg_start, S32 } } - F32 y_top = y + (F32)llround(font->getLineHeight()); + F32 y_top = y + (F32)llmath::llround(font->getLineHeight()); if( selection_left > seg_start ) { @@ -3995,7 +3995,7 @@ void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent) // propagate shape information to scrollbar mScrollbar->setDocSize( getLineCount() ); - S32 line_height = llround( mGLFont->getLineHeight() ); + S32 line_height = llmath::llround( mGLFont->getLineHeight() ); S32 page_lines = mTextRect.getHeight() / line_height; mScrollbar->setPageSize( page_lines ); } @@ -5162,7 +5162,7 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect } const llwchar * const text = mWText.c_str(); - const S32 line_height = llround(mGLFont->getLineHeight()); + const S32 line_height = llmath::llround(mGLFont->getLineHeight()); if (coord) { @@ -5265,7 +5265,7 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length) S32 LLTextEditor::getPreeditFontSize() const { - return llround(mGLFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); + return llmath::llround(mGLFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); } void LLTextEditor::setKeystrokeCallback(const keystroke_signal_t::slot_type& callback) diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index aab2df83f..39f4598c5 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -158,8 +158,8 @@ void LLUI::cleanupClass() void LLUI::setMousePositionScreen(S32 x, S32 y) { S32 screen_x, screen_y; - screen_x = llround((F32)x * getScaleFactor().mV[VX]); - screen_y = llround((F32)y * getScaleFactor().mV[VY]); + screen_x = llmath::llround((F32)x * getScaleFactor().mV[VX]); + screen_y = llmath::llround((F32)y * getScaleFactor().mV[VY]); LLView::getWindow()->setCursorPosition(LLCoordGL(screen_x, screen_y).convert()); } @@ -170,8 +170,8 @@ void LLUI::getMousePositionScreen(S32 *x, S32 *y) LLCoordWindow cursor_pos_window; getWindow()->getCursorPosition(&cursor_pos_window); LLCoordGL cursor_pos_gl(cursor_pos_window.convert()); - *x = llround((F32)cursor_pos_gl.mX / getScaleFactor().mV[VX]); - *y = llround((F32)cursor_pos_gl.mY / getScaleFactor().mV[VX]); + *x = llmath::llround((F32)cursor_pos_gl.mX / getScaleFactor().mV[VX]); + *y = llmath::llround((F32)cursor_pos_gl.mY / getScaleFactor().mV[VX]); } //static @@ -262,15 +262,15 @@ LLVector2 LLUI::getWindowSize() //static void LLUI::screenPointToGL(S32 screen_x, S32 screen_y, S32 *gl_x, S32 *gl_y) { - *gl_x = llround((F32)screen_x * getScaleFactor().mV[VX]); - *gl_y = llround((F32)screen_y * getScaleFactor().mV[VY]); + *gl_x = llmath::llround((F32)screen_x * getScaleFactor().mV[VX]); + *gl_y = llmath::llround((F32)screen_y * getScaleFactor().mV[VY]); } //static void LLUI::glPointToScreen(S32 gl_x, S32 gl_y, S32 *screen_x, S32 *screen_y) { - *screen_x = llround((F32)gl_x / getScaleFactor().mV[VX]); - *screen_y = llround((F32)gl_y / getScaleFactor().mV[VY]); + *screen_x = llmath::llround((F32)gl_x / getScaleFactor().mV[VX]); + *screen_y = llmath::llround((F32)gl_y / getScaleFactor().mV[VY]); } //static diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index d4c83d674..fe5d80bb1 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -821,9 +821,9 @@ void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data) // If parcel name is empty use Sim_name (x, y, z) for parcel label. else if (!parcel_data.sim_name.empty()) { - S32 region_x = llround(parcel_data.global_x) % REGION_WIDTH_UNITS; - S32 region_y = llround(parcel_data.global_y) % REGION_WIDTH_UNITS; - S32 region_z = llround(parcel_data.global_z); + S32 region_x = llmath::llround(parcel_data.global_x) % REGION_WIDTH_UNITS; + S32 region_y = llmath::llround(parcel_data.global_y) % REGION_WIDTH_UNITS; + S32 region_z = llmath::llround(parcel_data.global_z); label = llformat("%s (%d, %d, %d)", parcel_data.sim_name.c_str(), region_x, region_y, region_z); diff --git a/indra/newview/aihttpview.cpp b/indra/newview/aihttpview.cpp index d3de57171..5c0ac97a6 100644 --- a/indra/newview/aihttpview.cpp +++ b/indra/newview/aihttpview.cpp @@ -190,7 +190,7 @@ class AIGLHTTPHeaderBar : public LLView AIGLHTTPHeaderBar(std::string const& name, AIHTTPView* httpview) : LLView(name, FALSE), mHTTPView(httpview) { - sLineHeight = llround(LLFontGL::getFontMonospace()->getLineHeight()); + sLineHeight = llmath::llround(LLFontGL::getFontMonospace()->getLineHeight()); setRect(LLRect(0, 0, 200, sLineHeight * number_of_header_lines)); } diff --git a/indra/newview/llcloud.cpp b/indra/newview/llcloud.cpp index fcdfbecd4..12c260b87 100644 --- a/indra/newview/llcloud.cpp +++ b/indra/newview/llcloud.cpp @@ -189,7 +189,7 @@ void LLCloudGroup::updatePuffCount() return; } S32 i; - S32 target_puff_count = llround(CLOUD_DENSITY * mDensity); + S32 target_puff_count = llmath::llround(CLOUD_DENSITY * mDensity); target_puff_count = llmax(0, target_puff_count); target_puff_count = llmin(CLOUD_COUNT_MAX, target_puff_count); S32 current_puff_count = (S32) mCloudPuffs.size(); diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index 56fe132d3..449753b47 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -812,7 +812,7 @@ void LLDrawable::updateDistance(LLCamera& camera, bool force_update) } pos -= camera.getOrigin(); - mDistanceWRTCamera = llround(pos.magVec(), 0.01f); + mDistanceWRTCamera = llmath::llround(pos.magVec(), 0.01f); mVObjp->updateLOD(); } } diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 0a0a2d610..fcebf26e7 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -1309,7 +1309,7 @@ void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLI for( i = minimum; i <= maximum; i++ ) { F32 minus_one_to_one = F32(maximum - i) * twice_one_over_range - 1.f; - bias_and_scale_lut[i] = llclampb(llround(127 * minus_one_to_one * ARTIFICIAL_SCALE + 128)); + bias_and_scale_lut[i] = llclampb(llmath::llround(127 * minus_one_to_one * ARTIFICIAL_SCALE + 128)); } } else @@ -1318,7 +1318,7 @@ void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLI for( i = minimum; i <= maximum; i++ ) { F32 minus_one_to_one = F32(i - minimum) * twice_one_over_range - 1.f; - bias_and_scale_lut[i] = llclampb(llround(127 * minus_one_to_one * ARTIFICIAL_SCALE + 128)); + bias_and_scale_lut[i] = llclampb(llmath::llround(127 * minus_one_to_one * ARTIFICIAL_SCALE + 128)); } } diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp index 30864755c..eb360e120 100644 --- a/indra/newview/llfasttimerview.cpp +++ b/indra/newview/llfasttimerview.cpp @@ -256,7 +256,7 @@ BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask) if (hasMouseCapture()) { F32 lerp = llclamp(1.f - (F32) (x - mGraphRect.mLeft) / (F32) mGraphRect.getWidth(), 0.f, 1.f); - mScrollIndex = llround( lerp * (F32)(LLFastTimer::NamedTimer::HISTORY_NUM - MAX_VISIBLE_HISTORY)); + mScrollIndex = llmath::llround( lerp * (F32)(LLFastTimer::NamedTimer::HISTORY_NUM - MAX_VISIBLE_HISTORY)); mScrollIndex = llclamp( mScrollIndex, 0, LLFastTimer::getLastFrameIndex()); return TRUE; } @@ -298,9 +298,9 @@ BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask) } mToolTipRect.set(mBarStart[mHoverBarIndex][i], - mBarRect.mBottom + llround(((F32)(MAX_VISIBLE_HISTORY - mHoverBarIndex + 1)) * ((F32)mBarRect.getHeight() / ((F32)MAX_VISIBLE_HISTORY + 2.f))), + mBarRect.mBottom + llmath::llround(((F32)(MAX_VISIBLE_HISTORY - mHoverBarIndex + 1)) * ((F32)mBarRect.getHeight() / ((F32)MAX_VISIBLE_HISTORY + 2.f))), mBarEnd[mHoverBarIndex][i], - mBarRect.mBottom + llround((F32)(MAX_VISIBLE_HISTORY - mHoverBarIndex) * ((F32)mBarRect.getHeight() / ((F32)MAX_VISIBLE_HISTORY + 2.f)))); + mBarRect.mBottom + llmath::llround((F32)(MAX_VISIBLE_HISTORY - mHoverBarIndex) * ((F32)mBarRect.getHeight() / ((F32)MAX_VISIBLE_HISTORY + 2.f)))); } if ((*it)->getCollapsed()) @@ -432,7 +432,7 @@ void LLFastTimerView::draw() mAverageCyclesPerTimer = LLFastTimer::sTimerCalls == 0 ? 0 - : llround(lerp((F32)mAverageCyclesPerTimer, (F32)(LLFastTimer::sTimerCycles / (U64)LLFastTimer::sTimerCalls), 0.1f)); + : llmath::llround(lerp((F32)mAverageCyclesPerTimer, (F32)(LLFastTimer::sTimerCycles / (U64)LLFastTimer::sTimerCalls), 0.1f)); LLFastTimer::sTimerCycles = 0; LLFastTimer::sTimerCalls = 0; @@ -800,7 +800,7 @@ void LLFastTimerView::draw() ? (F32)idp->getCountAverage() / (F32)totalticks : (F32)idp->getHistoricalCount(tidx) / (F32)totalticks; - dx = llround(frac * (F32)barw); + dx = llmath::llround(frac * (F32)barw); S32 prev_delta_x = deltax.empty() ? 0 : deltax.back(); deltax.push_back(dx); diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp index cf97d4e82..1bcf79278 100644 --- a/indra/newview/llflexibleobject.cpp +++ b/indra/newview/llflexibleobject.cpp @@ -302,7 +302,7 @@ void LLVolumeImplFlexible::updateRenderRes() mRenderRes = (S32) (12.f*app_angle); #else //legacy behavior //number of segments only cares about z axis - F32 app_angle = llround((F32) atan2( mVO->getScale().mV[2]*2.f, drawablep->mDistanceWRTCamera) * RAD_TO_DEG, 0.01f); + F32 app_angle = llmath::llround((F32) atan2( mVO->getScale().mV[2]*2.f, drawablep->mDistanceWRTCamera) * RAD_TO_DEG, 0.01f); // Rendering sections increases with visible angle on the screen mRenderRes = (S32)(FLEXIBLE_OBJECT_MAX_SECTIONS*4*app_angle*DEG_TO_RAD/LLViewerCamera::getInstance()->getView()); diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp index b955e31fb..86ed6505f 100644 --- a/indra/newview/llfloaterbuyland.cpp +++ b/indra/newview/llfloaterbuyland.cpp @@ -386,9 +386,9 @@ void LLFloaterBuyLandUI::updateParcelInfo() } mParcelBillableArea = - llround(mRegion->getBillableFactor() * mParcelActualArea); + llmath::llround(mRegion->getBillableFactor() * mParcelActualArea); - mParcelSupportedObjects = llround( + mParcelSupportedObjects = llmath::llround( parcel->getMaxPrimCapacity() * parcel->getParcelPrimBonus()); // Can't have more than region max tasks, regardless of parcel // object bonus factor. diff --git a/indra/newview/llfloaterenvsettings.cpp b/indra/newview/llfloaterenvsettings.cpp index 09f6ff402..c54a62545 100644 --- a/indra/newview/llfloaterenvsettings.cpp +++ b/indra/newview/llfloaterenvsettings.cpp @@ -316,7 +316,7 @@ std::string LLFloaterEnvSettings::timeToString(F32 curTime) // get hours and minutes hours = (S32) (24.0 * curTime); curTime -= ((F32) hours / 24.0f); - min = llround(24.0f * 60.0f * curTime); + min = llmath::llround(24.0f * 60.0f * curTime); // handle case where it's 60 if(min == 60) diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index d76a43566..6c68e27d6 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -1203,7 +1203,7 @@ void LLPanelLandObjects::refresh() { S32 sw_max = parcel->getSimWideMaxPrimCapacity(); S32 sw_total = parcel->getSimWidePrimCount(); - S32 max = llround(parcel->getMaxPrimCapacity() * parcel->getParcelPrimBonus()); + S32 max = llmath::llround(parcel->getMaxPrimCapacity() * parcel->getParcelPrimBonus()); S32 total = parcel->getPrimCount(); S32 owned = parcel->getOwnerPrimCount(); S32 group = parcel->getGroupPrimCount(); @@ -2081,9 +2081,9 @@ void LLPanelLandOptions::refresh() else { mLocationText->setTextArg("[LANDING]",llformat("%d, %d, %d", - llround(pos.mV[VX]), - llround(pos.mV[VY]), - llround(pos.mV[VZ]))); + llmath::llround(pos.mV[VX]), + llmath::llround(pos.mV[VY]), + llmath::llround(pos.mV[VZ]))); } mSetBtn->setEnabled( can_change_landing_point ); diff --git a/indra/newview/llfloaterlandholdings.cpp b/indra/newview/llfloaterlandholdings.cpp index 4949678c1..405983590 100644 --- a/indra/newview/llfloaterlandholdings.cpp +++ b/indra/newview/llfloaterlandholdings.cpp @@ -226,8 +226,8 @@ void LLFloaterLandHoldings::processPlacesReply(LLMessageSystem* msg, void**) self->mActualArea += actual_area; self->mBillableArea += billable_area; - S32 region_x = llround(global_x) % REGION_WIDTH_UNITS; - S32 region_y = llround(global_y) % REGION_WIDTH_UNITS; + S32 region_x = llmath::llround(global_x) % REGION_WIDTH_UNITS; + S32 region_y = llmath::llround(global_y) % REGION_WIDTH_UNITS; std::string location; location = llformat("%s (%d, %d)", sim_name.c_str(), region_x, region_y); diff --git a/indra/newview/llfloaterpathfindingcharacters.cpp b/indra/newview/llfloaterpathfindingcharacters.cpp index d5e7fc089..d335b9566 100644 --- a/indra/newview/llfloaterpathfindingcharacters.cpp +++ b/indra/newview/llfloaterpathfindingcharacters.cpp @@ -219,7 +219,7 @@ LLSD LLFloaterPathfindingCharacters::buildCharacterScrollListItemData(const LLPa columns[2]["column"] = "owner"; columns[2]["value"] = getOwnerName(pCharacterPtr); - S32 cpuTime = llround(pCharacterPtr->getCPUTime()); + S32 cpuTime = llmath::llround(pCharacterPtr->getCPUTime()); std::string cpuTimeString = llformat("%d", cpuTime); LLStringUtil::format_map_t string_args; string_args["[CPU_TIME]"] = cpuTimeString; diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index 9d7e2c0be..ceda09814 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -493,16 +493,16 @@ void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail if (mAspectRatio > window_aspect_ratio) { // trim off top and bottom - S32 height_diff = llround(getRect().getHeight() - (F32)getRect().getWidth() / mAspectRatio); - S32 half_height_diff = llround((getRect().getHeight() - (F32)getRect().getWidth() / mAspectRatio) * 0.5); + S32 height_diff = llmath::llround(getRect().getHeight() - (F32)getRect().getWidth() / mAspectRatio); + S32 half_height_diff = llmath::llround((getRect().getHeight() - (F32)getRect().getWidth() / mAspectRatio) * 0.5); rect.mBottom += half_height_diff; rect.mTop -= height_diff - half_height_diff; } else if (mAspectRatio < window_aspect_ratio) { // trim off left and right - S32 width_diff = llround(getRect().getWidth() - (F32)getRect().getHeight() * mAspectRatio); - S32 half_width_diff = llround((getRect().getWidth() - (F32)getRect().getHeight() * mAspectRatio) * 0.5f); + S32 width_diff = llmath::llround(getRect().getWidth() - (F32)getRect().getHeight() * mAspectRatio); + S32 half_width_diff = llmath::llround((getRect().getWidth() - (F32)getRect().getHeight() * mAspectRatio) * 0.5f); rect.mLeft += half_width_diff; rect.mRight -= width_diff - half_width_diff; } @@ -639,9 +639,9 @@ void LLSnapshotLivePreview::draw() LLLocalClipRect clip(getLocalRect()); { // draw diagonal stripe with gradient that passes over screen - S32 x1 = gViewerWindow->getWindowWidthScaled() * llround((clamp_rescale(shine_interp, 0.f, 1.f, -1.f - SHINE_WIDTH, 1.f))); - S32 x2 = x1 + llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH); - S32 x3 = x2 + llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH); + S32 x1 = gViewerWindow->getWindowWidthScaled() * llmath::llround((clamp_rescale(shine_interp, 0.f, 1.f, -1.f - SHINE_WIDTH, 1.f))); + S32 x2 = x1 + llmath::llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH); + S32 x3 = x2 + llmath::llround(gViewerWindow->getWindowWidthScaled() * SHINE_WIDTH); S32 y1 = 0; S32 y2 = gViewerWindow->getWindowHeightScaled(); @@ -719,7 +719,7 @@ void LLSnapshotLivePreview::draw() gGL.pushMatrix(); { LLRect const& rect = mFallFullScreenImageRect; - gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom - llround(getRect().getHeight() * 2.f * (fall_interp * fall_interp)), 0.f); + gGL.translatef((F32)rect.mLeft, (F32)rect.mBottom - llmath::llround(getRect().getHeight() * 2.f * (fall_interp * fall_interp)), 0.f); gGL.rotatef(-45.f * fall_interp, 0.f, 0.f, 1.f); gGL.begin(LLRender::QUADS); { @@ -772,13 +772,13 @@ BOOL LLSnapshotLivePreview::setThumbnailImageSize() { // image too wide, shrink to width mThumbnailWidth = max_width; - mThumbnailHeight = llround((F32)max_width / window_aspect_ratio); + mThumbnailHeight = llmath::llround((F32)max_width / window_aspect_ratio); } else { // image too tall, shrink to height mThumbnailHeight = max_height; - mThumbnailWidth = llround((F32)max_height * window_aspect_ratio); + mThumbnailWidth = llmath::llround((F32)max_height * window_aspect_ratio); } if(mThumbnailWidth > window_width || mThumbnailHeight > window_height) @@ -790,11 +790,11 @@ BOOL LLSnapshotLivePreview::setThumbnailImageSize() F32 ratio = mAspectRatio * window_height / window_width; if(ratio > 1.f) { - top = llround(top / ratio); + top = llmath::llround(top / ratio); } else { - right = llround(right * ratio); + right = llmath::llround(right * ratio); } left = (mThumbnailWidth - right + 1) / 2; bottom = (mThumbnailHeight - top + 1) / 2; @@ -991,8 +991,8 @@ LLSnapshotLivePreview::EAspectSizeProblem LLSnapshotLivePreview::getAspectSizePr // We need an image with the aspect mAspectRatio (from which mWidth and mHeight were derived). // The current aspect ratio of mRawSnapshot. This should be (almost) equal to window_width / window_height, - // since these values are calculated in rawRawSnapshot with llround(window_width * scale_factor) and - // llround(window_height * scale_factor) respectively (since we set uncrop = true). + // since these values are calculated in rawRawSnapshot with llmath::llround(window_width * scale_factor) and + // llmath::llround(window_height * scale_factor) respectively (since we set uncrop = true). F32 raw_aspect = (F32)mRawSnapshotWidth / mRawSnapshotHeight; // The smaller dimension might have been rounded up to 0.5 up or down. Calculate upper and lower limits. F32 lower_raw_aspect = (mRawSnapshotWidth - 0.5) / (mRawSnapshotHeight + 0.5); @@ -1017,7 +1017,7 @@ LLSnapshotLivePreview::EAspectSizeProblem LLSnapshotLivePreview::getAspectSizePr height_out = mRawSnapshotHeight; if (mAspectRatio < lower_raw_aspect) { - width_out = llround(width_out * mAspectRatio / raw_aspect); + width_out = llmath::llround(width_out * mAspectRatio / raw_aspect); if (width_out < mRawSnapshotWidth) { // Only set this to false when there is actually something to crop. @@ -1033,7 +1033,7 @@ LLSnapshotLivePreview::EAspectSizeProblem LLSnapshotLivePreview::getAspectSizePr } else if (mAspectRatio > upper_raw_aspect) { - height_out = llround(height_out * raw_aspect / mAspectRatio); + height_out = llmath::llround(height_out * raw_aspect / mAspectRatio); if (height_out < mRawSnapshotHeight) { if (!allow_vertical_crop) @@ -2403,8 +2403,8 @@ void LLFloaterSnapshot::Impl::keepAspect(LLFloaterSnapshot* view, bool on, bool LLSnapshotLivePreview* previewp = getPreviewView(); if (previewp) { - S32 w = llround(view->childGetValue("snapshot_width").asReal(), 1.0); - S32 h = llround(view->childGetValue("snapshot_height").asReal(), 1.0); + S32 w = llmath::llround(view->childGetValue("snapshot_width").asReal(), 1.0); + S32 h = llmath::llround(view->childGetValue("snapshot_height").asReal(), 1.0); gSavedSettings.setS32(lastSnapshotWidthName(), w); gSavedSettings.setS32(lastSnapshotHeightName(), h); comboSetCustom(view, previewp->resolutionComboName()); @@ -2464,8 +2464,8 @@ void LLFloaterSnapshot::Impl::updateResolution(LLUICtrl* ctrl, void* data, bool // The size is actually the source aspect now, encoded in the width. F32 source_aspect = width / 630.f; // Use the size of the current window, cropped to the required aspect. - width = llmin(gViewerWindow->getWindowDisplayWidth(), llround(gViewerWindow->getWindowDisplayHeight() * source_aspect)); - height = llmin(gViewerWindow->getWindowDisplayHeight(), llround(gViewerWindow->getWindowDisplayWidth() / source_aspect)); + width = llmin(gViewerWindow->getWindowDisplayWidth(), llmath::llround(gViewerWindow->getWindowDisplayHeight() * source_aspect)); + height = llmin(gViewerWindow->getWindowDisplayHeight(), llmath::llround(gViewerWindow->getWindowDisplayWidth() / source_aspect)); previewp->setSize(width, height); } else @@ -2652,8 +2652,8 @@ void LLFloaterSnapshot::Impl::enforceResolution(LLFloaterSnapshot* floater, F32 nw = llmin(nw, nh * new_aspect); nh = llmin(nh, nw / new_aspect); // Round off to nearest integer. - S32 new_width = llround(nw); - S32 new_height = llround(nh); + S32 new_width = llmath::llround(nw); + S32 new_height = llmath::llround(nh); gSavedSettings.setS32(lastSnapshotWidthName(), new_width); gSavedSettings.setS32(lastSnapshotHeightName(), new_height); @@ -2754,8 +2754,8 @@ void LLFloaterSnapshot::Impl::onCommitCustomResolution(LLUICtrl *ctrl, void* dat LLSpinCtrl* width_spinner = view->getChild("snapshot_width"); LLSpinCtrl* height_spinner = view->getChild("snapshot_height"); - S32 w = llround((F32)width_spinner->getValue().asReal(), 1.0f); - S32 h = llround((F32)height_spinner->getValue().asReal(), 1.0f); + S32 w = llmath::llround((F32)width_spinner->getValue().asReal(), 1.0f); + S32 h = llmath::llround((F32)height_spinner->getValue().asReal(), 1.0f); LLSnapshotLivePreview* previewp = getPreviewView(); if (previewp) @@ -2782,12 +2782,12 @@ void LLFloaterSnapshot::Impl::onCommitCustomResolution(LLUICtrl *ctrl, void* dat if (h == curh) { // Width was changed. Change height to keep aspect constant. - h = llround(w / aspect); + h = llmath::llround(w / aspect); } else { // Height was changed. Change width to keep aspect constant. - w = llround(h * aspect); + w = llmath::llround(h * aspect); } } width_spinner->forceSetValue(LLSD::Real(w)); diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 1d33194a0..c930fa461 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -710,9 +710,9 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global) // std::string full_name = llformat("%s (%d, %d, %d)", sim_name.c_str(), - llround(region_x), - llround(region_y), - llround((F32)pos_global.mdV[VZ])); + llmath::llround(region_x), + llmath::llround(region_y), + llmath::llround((F32)pos_global.mdV[VZ])); std::string tooltip(""); mTrackedStatus = LLTracker::TRACKING_LOCATION; diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 8e36725c8..14259fe99 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -535,7 +535,7 @@ S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_gen updateRenamerPosition(); mTargetHeight = (F32)target_height; - return llround(mTargetHeight); + return llmath::llround(mTargetHeight); } const std::string LLFolderView::getFilterSubString(BOOL trim) @@ -2041,7 +2041,7 @@ void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constr LLRect item_scrolled_rect; // item position relative to display area of scroller S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight(); - S32 label_height = llround(getLabelFontForStyle(mLabelStyle)->getLineHeight()); + S32 label_height = llmath::llround(getLabelFontForStyle(mLabelStyle)->getLineHeight()); // when navigating with keyboard, only move top of opened folder on screen, otherwise show whole folder S32 max_height_to_show = item->isOpen() && mScrollContainer->hasFocus() ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight(); diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index 52c6cd998..3014bf180 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -1088,7 +1088,7 @@ void LLFolderViewItem::draw() if (filter_string_length > 0 && (mRoot->getSearchType() & 1)) { std::string combined_string = mLabel + mLabelSuffix; - S32 left = llround(text_left) + font->getWidth(combined_string, 0, mStringMatchOffset) - 1; + S32 left = llmath::llround(text_left) + font->getWidth(combined_string, 0, mStringMatchOffset) - 1; S32 right = left + font->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2; S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); S32 top = getRect().getHeight() - TOP_PAD; @@ -1225,7 +1225,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) { S32 child_width = *width; S32 child_height = 0; - S32 child_top = parent_item_height - llround(running_height); + S32 child_top = parent_item_height - llmath::llround(running_height); target_height += folderp->arrange( &child_width, &child_height, filter_generation ); @@ -1251,7 +1251,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) { S32 child_width = *width; S32 child_height = 0; - S32 child_top = parent_item_height - llround(running_height); + S32 child_top = parent_item_height - llmath::llround(running_height); target_height += itemp->arrange( &child_width, &child_height, filter_generation ); // don't change width, as this item is as wide as its parent folder by construction @@ -1288,7 +1288,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) folders_t::iterator fit = iter++; // number of pixels that bottom of folder label is from top of parent folder if (getRect().getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight() - > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) + > llmath::llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) { // hide if beyond current folder height (*fit)->setVisible(FALSE); @@ -1301,7 +1301,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) items_t::iterator iit = iter++; // number of pixels that bottom of item label is from top of parent folder if (getRect().getHeight() - (*iit)->getRect().mBottom - > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) + > llmath::llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) { (*iit)->setVisible(FALSE); } @@ -1313,12 +1313,12 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) } // don't change width as this item is already as wide as its parent folder - reshape(getRect().getWidth(),llround(mCurHeight)); + reshape(getRect().getWidth(),llmath::llround(mCurHeight)); // pass current height value back to parent - *height = llround(mCurHeight); + *height = llmath::llround(mCurHeight); - return llround(mTargetHeight); + return llmath::llround(mTargetHeight); } BOOL LLFolderViewFolder::needsArrange() diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp index 53caaee64..c951fe953 100644 --- a/indra/newview/llglsandbox.cpp +++ b/indra/newview/llglsandbox.cpp @@ -100,10 +100,10 @@ void LLToolSelectRect::handleRectangleSelection(S32 x, S32 y, MASK mask) S32 top = llmax(y, mDragStartY); S32 bottom =llmin(y, mDragStartY); - left = llround((F32) left * LLUI::getScaleFactor().mV[VX]); - right = llround((F32) right * LLUI::getScaleFactor().mV[VX]); - top = llround((F32) top * LLUI::getScaleFactor().mV[VY]); - bottom = llround((F32) bottom * LLUI::getScaleFactor().mV[VY]); + left = llmath::llround((F32) left * LLUI::getScaleFactor().mV[VX]); + right = llmath::llround((F32) right * LLUI::getScaleFactor().mV[VX]); + top = llmath::llround((F32) top * LLUI::getScaleFactor().mV[VY]); + bottom = llmath::llround((F32) bottom * LLUI::getScaleFactor().mV[VY]); F32 old_far_plane = LLViewerCamera::getInstance()->getFar(); F32 old_near_plane = LLViewerCamera::getInstance()->getNear(); diff --git a/indra/newview/llmanip.cpp b/indra/newview/llmanip.cpp index f83e14e9b..c0f606414 100644 --- a/indra/newview/llmanip.cpp +++ b/indra/newview/llmanip.cpp @@ -538,8 +538,8 @@ void LLManip::renderTickValue(const LLVector3& pos, F32 value, const std::string std::string val_string; std::string fraction_string; - F32 val_to_print = llround(value, 0.001f); - S32 fractional_portion = llround(fmodf(llabs(val_to_print), 1.f) * 100.f); + F32 val_to_print = llmath::llround(value, 0.001f); + S32 fractional_portion = llmath::llround(fmodf(llabs(val_to_print), 1.f) * 100.f); if (val_to_print < 0.f) { if (fractional_portion == 0) diff --git a/indra/newview/llmaniprotate.cpp b/indra/newview/llmaniprotate.cpp index b04c0260e..5c160e5b6 100644 --- a/indra/newview/llmaniprotate.cpp +++ b/indra/newview/llmaniprotate.cpp @@ -373,9 +373,9 @@ void LLManipRotate::render() LLQuaternion object_rot = first_object->getRotationEdit(); object_rot.getEulerAngles(&(euler_angles.mV[VX]), &(euler_angles.mV[VY]), &(euler_angles.mV[VZ])); euler_angles *= RAD_TO_DEG; - euler_angles.mV[VX] = llround(fmodf(euler_angles.mV[VX] + 360.f, 360.f), 0.05f); - euler_angles.mV[VY] = llround(fmodf(euler_angles.mV[VY] + 360.f, 360.f), 0.05f); - euler_angles.mV[VZ] = llround(fmodf(euler_angles.mV[VZ] + 360.f, 360.f), 0.05f); + euler_angles.mV[VX] = llmath::llround(fmodf(euler_angles.mV[VX] + 360.f, 360.f), 0.05f); + euler_angles.mV[VY] = llmath::llround(fmodf(euler_angles.mV[VY] + 360.f, 360.f), 0.05f); + euler_angles.mV[VZ] = llmath::llround(fmodf(euler_angles.mV[VZ] + 360.f, 360.f), 0.05f); renderXYZ(euler_angles); } @@ -1500,7 +1500,7 @@ LLQuaternion LLManipRotate::dragConstrained( S32 x, S32 y ) F32 mouse_angle = fmodf(atan2(projected_mouse * axis1, projected_mouse * axis2) * RAD_TO_DEG + 360.f, 360.f); F32 relative_mouse_angle = fmodf(mouse_angle + (SNAP_ANGLE_DETENTE / 2), SNAP_ANGLE_INCREMENT); - //fmodf(llround(mouse_angle * RAD_TO_DEG, 7.5f) + 360.f, 360.f); + //fmodf(llmath::llround(mouse_angle * RAD_TO_DEG, 7.5f) + 360.f, 360.f); LLVector3 object_axis; getObjectAxisClosestToMouse(object_axis); @@ -1584,7 +1584,7 @@ LLQuaternion LLManipRotate::dragConstrained( S32 x, S32 y ) F32 mouse_angle = fmodf(atan2(projected_mouse * axis1, projected_mouse * axis2) * RAD_TO_DEG + 360.f, 360.f); F32 relative_mouse_angle = fmodf(mouse_angle + (SNAP_ANGLE_DETENTE / 2), SNAP_ANGLE_INCREMENT); - //fmodf(llround(mouse_angle * RAD_TO_DEG, 7.5f) + 360.f, 360.f); + //fmodf(llmath::llround(mouse_angle * RAD_TO_DEG, 7.5f) + 360.f, 360.f); LLVector3 object_axis; getObjectAxisClosestToMouse(object_axis); diff --git a/indra/newview/llmanipscale.cpp b/indra/newview/llmanipscale.cpp index 3ef134ee0..1a40a9e12 100644 --- a/indra/newview/llmanipscale.cpp +++ b/indra/newview/llmanipscale.cpp @@ -1559,8 +1559,8 @@ void LLManipScale::updateSnapGuides(const LLBBox& bbox) mScaleSnapUnit1 = mScaleSnapUnit1 / (mSnapDir1 * mScaleDir); mScaleSnapUnit2 = mScaleSnapUnit2 / (mSnapDir2 * mScaleDir); - mTickPixelSpacing1 = llround((F32)MIN_DIVISION_PIXEL_WIDTH / (mScaleDir % mSnapGuideDir1).length()); - mTickPixelSpacing2 = llround((F32)MIN_DIVISION_PIXEL_WIDTH / (mScaleDir % mSnapGuideDir2).length()); + mTickPixelSpacing1 = llmath::llround((F32)MIN_DIVISION_PIXEL_WIDTH / (mScaleDir % mSnapGuideDir1).length()); + mTickPixelSpacing2 = llmath::llround((F32)MIN_DIVISION_PIXEL_WIDTH / (mScaleDir % mSnapGuideDir2).length()); if (uniform) { @@ -1632,8 +1632,8 @@ void LLManipScale::renderSnapGuides(const LLBBox& bbox) F32 grid_offset2 = fmodf(dist_grid_axis, smallest_subdivision2); // how many smallest grid units are we away from largest grid scale? - S32 sub_div_offset_1 = llround(fmod(dist_grid_axis - grid_offset1, mScaleSnapUnit1 / sGridMinSubdivisionLevel) / smallest_subdivision1); - S32 sub_div_offset_2 = llround(fmod(dist_grid_axis - grid_offset2, mScaleSnapUnit2 / sGridMinSubdivisionLevel) / smallest_subdivision2); + S32 sub_div_offset_1 = llmath::llround(fmod(dist_grid_axis - grid_offset1, mScaleSnapUnit1 / sGridMinSubdivisionLevel) / smallest_subdivision1); + S32 sub_div_offset_2 = llmath::llround(fmod(dist_grid_axis - grid_offset2, mScaleSnapUnit2 / sGridMinSubdivisionLevel) / smallest_subdivision2); S32 num_ticks_per_side1 = llmax(1, lltrunc(0.5f * mSnapGuideLength / smallest_subdivision1)); S32 num_ticks_per_side2 = llmax(1, lltrunc(0.5f * mSnapGuideLength / smallest_subdivision2)); @@ -1687,7 +1687,7 @@ void LLManipScale::renderSnapGuides(const LLBBox& bbox) LLVector2 screen_translate_axis(llabs(mScaleDir * LLViewerCamera::getInstance()->getLeftAxis()), llabs(mScaleDir * LLViewerCamera::getInstance()->getUpAxis())); screen_translate_axis.normalize(); - S32 tick_label_spacing = llround(screen_translate_axis * sTickLabelSpacing); + S32 tick_label_spacing = llmath::llround(screen_translate_axis * sTickLabelSpacing); for (pass = 0; pass < 3; pass++) { @@ -1767,8 +1767,8 @@ void LLManipScale::renderSnapGuides(const LLBBox& bbox) stop_tick = llmin(max_ticks1, num_ticks_per_side1); F32 grid_resolution = mObjectSelection->getSelectType() == SELECT_TYPE_HUD ? 0.25f : llmax(gSavedSettings.getF32("GridResolution"), 0.001f); - S32 label_sub_div_offset_1 = llround(fmod(dist_grid_axis - grid_offset1, mScaleSnapUnit1 * 32.f) / smallest_subdivision1); - S32 label_sub_div_offset_2 = llround(fmod(dist_grid_axis - grid_offset2, mScaleSnapUnit2 * 32.f) / smallest_subdivision2); + S32 label_sub_div_offset_1 = llmath::llround(fmod(dist_grid_axis - grid_offset1, mScaleSnapUnit1 * 32.f) / smallest_subdivision1); + S32 label_sub_div_offset_2 = llmath::llround(fmod(dist_grid_axis - grid_offset2, mScaleSnapUnit2 * 32.f) / smallest_subdivision2); for (S32 i = start_tick; i <= stop_tick; i++) { diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp index b066e5275..3be734b62 100644 --- a/indra/newview/llmaniptranslate.cpp +++ b/indra/newview/llmaniptranslate.cpp @@ -1263,7 +1263,7 @@ void LLManipTranslate::renderSnapGuides() // find distance to nearest smallest grid unit F32 offset_nearest_grid_unit = fmodf(dist_grid_axis, smallest_grid_unit_scale); // how many smallest grid units are we away from largest grid scale? - S32 sub_div_offset = llround(fmodf(dist_grid_axis - offset_nearest_grid_unit, getMinGridScale() / sGridMinSubdivisionLevel) / smallest_grid_unit_scale); + S32 sub_div_offset = llmath::llround(fmodf(dist_grid_axis - offset_nearest_grid_unit, getMinGridScale() / sGridMinSubdivisionLevel) / smallest_grid_unit_scale); S32 num_ticks_per_side = llmax(1, llfloor(0.5f * guide_size_meters / smallest_grid_unit_scale)); LLGLDepthTest gls_depth(GL_FALSE); @@ -1377,12 +1377,12 @@ void LLManipTranslate::renderSnapGuides() } } - sub_div_offset = llround(fmod(dist_grid_axis - offset_nearest_grid_unit, getMinGridScale() * 32.f) / smallest_grid_unit_scale); + sub_div_offset = llmath::llround(fmod(dist_grid_axis - offset_nearest_grid_unit, getMinGridScale() * 32.f) / smallest_grid_unit_scale); LLVector2 screen_translate_axis(llabs(translate_axis * LLViewerCamera::getInstance()->getLeftAxis()), llabs(translate_axis * LLViewerCamera::getInstance()->getUpAxis())); screen_translate_axis.normVec(); - S32 tick_label_spacing = llround(screen_translate_axis * sTickLabelSpacing); + S32 tick_label_spacing = llmath::llround(screen_translate_axis * sTickLabelSpacing); // render tickmark values for (S32 i = -num_ticks_per_side; i <= num_ticks_per_side; i++) @@ -1420,7 +1420,7 @@ void LLManipTranslate::renderSnapGuides() F32 offset_val = 0.5f * tick_offset.mV[ARROW_TO_AXIS[mManipPart]] / getMinGridScale(); EGridMode grid_mode = LLSelectMgr::getInstance()->getGridMode(); F32 text_highlight = 0.8f; - if(i - llround(offset_nearest_grid_unit / smallest_grid_unit_scale) == 0 && mInSnapRegime) + if(i - llmath::llround(offset_nearest_grid_unit / smallest_grid_unit_scale) == 0 && mInSnapRegime) { text_highlight = 1.f; } diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index 80ed404d3..02189151d 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -120,8 +120,8 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) : if(!getDecoupleTextureSize()) { - S32 screen_width = llround((F32)getRect().getWidth() * LLUI::getScaleFactor().mV[VX]); - S32 screen_height = llround((F32)getRect().getHeight() * LLUI::getScaleFactor().mV[VY]); + S32 screen_width = llmath::llround((F32)getRect().getWidth() * LLUI::getScaleFactor().mV[VX]); + S32 screen_height = llmath::llround((F32)getRect().getHeight() * LLUI::getScaleFactor().mV[VY]); setTextureSize(screen_width, screen_height); } @@ -464,8 +464,8 @@ void LLMediaCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) { if(!getDecoupleTextureSize()) { - S32 screen_width = llround((F32)width * LLUI::getScaleFactor().mV[VX]); - S32 screen_height = llround((F32)height * LLUI::getScaleFactor().mV[VY]); + S32 screen_width = llmath::llround((F32)width * LLUI::getScaleFactor().mV[VX]); + S32 screen_height = llmath::llround((F32)height * LLUI::getScaleFactor().mV[VY]); // when floater is minimized, these sizes are negative if ( screen_height > 0 && screen_width > 0 ) @@ -794,13 +794,13 @@ void LLMediaCtrl::draw() { // max width, adjusted height width = r.getWidth(); - height = llmin(llmax(llround(width / media_aspect), 0), r.getHeight()); + height = llmin(llmax(llmath::llround(width / media_aspect), 0), r.getHeight()); } else { // max height, adjusted width height = r.getHeight(); - width = llmin(llmax(llround(height * media_aspect), 0), r.getWidth()); + width = llmin(llmax(llmath::llround(height * media_aspect), 0), r.getWidth()); } } else @@ -820,10 +820,10 @@ void LLMediaCtrl::draw() /*if (mIgnoreUIScale) { - x_offset = llround((F32)x_offset * LLUI::getScaleFactor().mV[VX]); - y_offset = llround((F32)y_offset * LLUI::getScaleFactor().mV[VY]); - width = llround((F32)width * LLUI::getScaleFactor().mV[VX]); - height = llround((F32)height * LLUI::getScaleFactor().mV[VY]); + x_offset = llmath::llround((F32)x_offset * LLUI::getScaleFactor().mV[VX]); + y_offset = llmath::llround((F32)y_offset * LLUI::getScaleFactor().mV[VY]); + width = llmath::llround((F32)width * LLUI::getScaleFactor().mV[VX]); + height = llmath::llround((F32)height * LLUI::getScaleFactor().mV[VY]); }*/ // draw the browser @@ -894,14 +894,14 @@ void LLMediaCtrl::convertInputCoords(S32& x, S32& y) coords_opengl = mMediaSource->getMediaPlugin()->getTextureCoordsOpenGL(); } - x = llround((F32)x * LLUI::getScaleFactor().mV[VX]); + x = llmath::llround((F32)x * LLUI::getScaleFactor().mV[VX]); if ( ! coords_opengl ) { - y = llround((F32)(y) * LLUI::getScaleFactor().mV[VY]); + y = llmath::llround((F32)(y) * LLUI::getScaleFactor().mV[VY]); } else { - y = llround((F32)(getRect().getHeight() - y) * LLUI::getScaleFactor().mV[VY]); + y = llmath::llround((F32)(getRect().getHeight() - y) * LLUI::getScaleFactor().mV[VY]); }; } diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index 93b204759..40ba95dd7 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -314,7 +314,7 @@ void LLNetMap::draw() static const LLCachedControl live_region_color(gColors, "NetMapLiveRegion"); static const LLCachedControl dead_region_color(gColors, "NetMapDeadRegion"); // Aurora Sim - //S32 region_width = llround(LLWorld::getInstance()->getRegionWidthInMeters()); + //S32 region_width = llmath::llround(LLWorld::getInstance()->getRegionWidthInMeters()); S32 region_width = REGION_WIDTH_UNITS; // Aurora Sim @@ -638,8 +638,8 @@ void LLNetMap::draw() (pos_map.mV[VX] >= getRect().getWidth()) || (pos_map.mV[VY] >= getRect().getHeight()) ) { - S32 x = llround( pos_map.mV[VX] ); - S32 y = llround( pos_map.mV[VY] ); + S32 x = llmath::llround( pos_map.mV[VX] ); + S32 y = llmath::llround( pos_map.mV[VY] ); LLWorldMapView::drawTrackingCircle( getRect(), x, y, color, 1, 10); } @@ -675,13 +675,13 @@ void LLNetMap::draw() } pos_map = globalPosToView(gAgent.getPositionGlobal()); - S32 dot_width = llround(mDotRadius * 2.f); + S32 dot_width = llmath::llround(mDotRadius * 2.f); LLUIImagePtr you = LLWorldMapView::sAvatarYouLargeImage; if (you) { - you->draw(llround(pos_map.mV[VX] - mDotRadius), - llround(pos_map.mV[VY] - mDotRadius), + you->draw(llmath::llround(pos_map.mV[VX] - mDotRadius), + llmath::llround(pos_map.mV[VY] - mDotRadius), dot_width, dot_width); @@ -844,8 +844,8 @@ void LLNetMap::drawTracking(const LLVector3d& pos_global, const LLColor4& color, { if (draw_arrow) { - S32 x = llround( pos_local.mV[VX] ); - S32 y = llround( pos_local.mV[VY] ); + S32 x = llmath::llround( pos_local.mV[VX] ); + S32 y = llmath::llround( pos_local.mV[VY] ); LLWorldMapView::drawTrackingCircle( getRect(), x, y, color, 1, 10 ); LLWorldMapView::drawTrackingArrow( getRect(), x, y, color ); } @@ -861,8 +861,8 @@ void LLNetMap::drawTracking(const LLVector3d& pos_global, const LLColor4& color, LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y ) { - x -= llround(getRect().getWidth() / 2 + mCurPan.mV[VX]); - y -= llround(getRect().getHeight() / 2 + mCurPan.mV[VY]); + x -= llmath::llround(getRect().getWidth() / 2 + mCurPan.mV[VX]); + y -= llmath::llround(getRect().getHeight() / 2 + mCurPan.mV[VY]); LLVector3 pos_local( (F32)x, (F32)y, 0 ); @@ -1008,8 +1008,8 @@ void LLNetMap::setDirectionPos( LLTextBox* text_box, F32 rotation ) // Inset by a little to account for position display. radius -= 8.f; - text_box->setOrigin(llround(half_width + radius * cos( rotation )), - llround(half_height + radius * sin( rotation ))); + text_box->setOrigin(llmath::llround(half_width + radius * cos( rotation )), + llmath::llround(half_height + radius * sin( rotation ))); } void LLNetMap::updateMinorDirections() @@ -1038,7 +1038,7 @@ void LLNetMap::renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U & const F32 MAX_RADIUS = 256.0f; F32 radius_clamped = llmin(radius_meters, MAX_RADIUS); - S32 diameter_pixels = llround(2 * radius_clamped * mObjectMapTPM); + S32 diameter_pixels = llmath::llround(2 * radius_clamped * mObjectMapTPM); renderPoint( local_pos, color, diameter_pixels ); } @@ -1054,8 +1054,8 @@ void LLNetMap::renderPoint(const LLVector3 &pos_local, const LLColor4U &color, const S32 image_width = (S32)mObjectImagep->getWidth(); const S32 image_height = (S32)mObjectImagep->getHeight(); - S32 x_offset = llround(pos_local.mV[VX] * mObjectMapTPM + image_width / 2); - S32 y_offset = llround(pos_local.mV[VY] * mObjectMapTPM + image_height / 2); + S32 x_offset = llmath::llround(pos_local.mV[VX] * mObjectMapTPM + image_width / 2); + S32 y_offset = llmath::llround(pos_local.mV[VY] * mObjectMapTPM + image_height / 2); if ((x_offset < 0) || (x_offset >= image_width)) { @@ -1135,25 +1135,25 @@ void LLNetMap::renderPropertyLinesForRegion(const LLViewerRegion* pRegion, const const S32 imgHeight = (S32)mParcelImagep->getHeight(); const LLVector3 originLocal(pRegion->getOriginGlobal() - mParcelImageCenterGlobal); - const S32 originX = llround(originLocal.mV[VX] * mObjectMapTPM + imgWidth / 2); - const S32 originY = llround(originLocal.mV[VY] * mObjectMapTPM + imgHeight / 2); + const S32 originX = llmath::llround(originLocal.mV[VX] * mObjectMapTPM + imgWidth / 2); + const S32 originY = llmath::llround(originLocal.mV[VY] * mObjectMapTPM + imgHeight / 2); U32* pTextureData = (U32*)mParcelRawImagep->getData(); // // Draw the north and east region borders // - const S32 borderY = originY + llround(REGION_WIDTH_METERS * mObjectMapTPM); + const S32 borderY = originY + llmath::llround(REGION_WIDTH_METERS * mObjectMapTPM); if ( (borderY >= 0) && (borderY < imgHeight) ) { - S32 curX = llclamp(originX, 0, imgWidth), endX = llclamp(originX + llround(REGION_WIDTH_METERS * mObjectMapTPM), 0, imgWidth - 1); + S32 curX = llclamp(originX, 0, imgWidth), endX = llclamp(originX + llmath::llround(REGION_WIDTH_METERS * mObjectMapTPM), 0, imgWidth - 1); for (; curX <= endX; curX++) pTextureData[borderY * imgWidth + curX] = clrOverlay.mAll; } - const S32 borderX = originX + llround(REGION_WIDTH_METERS * mObjectMapTPM); + const S32 borderX = originX + llmath::llround(REGION_WIDTH_METERS * mObjectMapTPM); if ( (borderX >= 0) && (borderX < imgWidth) ) { - S32 curY = llclamp(originY, 0, imgHeight), endY = llclamp(originY + llround(REGION_WIDTH_METERS * mObjectMapTPM), 0, imgHeight - 1); + S32 curY = llclamp(originY, 0, imgHeight), endY = llclamp(originY + llmath::llround(REGION_WIDTH_METERS * mObjectMapTPM), 0, imgHeight - 1); for (; curY <= endY; curY++) pTextureData[curY * imgWidth + borderX] = clrOverlay.mAll; } @@ -1177,17 +1177,17 @@ void LLNetMap::renderPropertyLinesForRegion(const LLViewerRegion* pRegion, const if ( (!fForSale) && (!fCollision) && (0 == (overlay & (PARCEL_SOUTH_LINE | PARCEL_WEST_LINE))) ) continue; - const S32 posX = originX + llround(idxCol * GRID_STEP * mObjectMapTPM); - const S32 posY = originY + llround(idxRow * GRID_STEP * mObjectMapTPM); + const S32 posX = originX + llmath::llround(idxCol * GRID_STEP * mObjectMapTPM); + const S32 posY = originY + llmath::llround(idxRow * GRID_STEP * mObjectMapTPM); static LLCachedControl s_fForSaleParcels(gSavedSettings, "MiniMapForSaleParcels"); static LLCachedControl s_fShowCollisionParcels(gSavedSettings, "MiniMapCollisionParcels"); if ( ((s_fForSaleParcels) && (fForSale)) || ((s_fShowCollisionParcels) && (fCollision)) ) { - S32 curY = llclamp(posY, 0, imgHeight), endY = llclamp(posY + llround(GRID_STEP * mObjectMapTPM), 0, imgHeight - 1); + S32 curY = llclamp(posY, 0, imgHeight), endY = llclamp(posY + llmath::llround(GRID_STEP * mObjectMapTPM), 0, imgHeight - 1); for (; curY <= endY; curY++) { - S32 curX = llclamp(posX, 0, imgWidth) , endX = llclamp(posX + llround(GRID_STEP * mObjectMapTPM), 0, imgWidth - 1); + S32 curX = llclamp(posX, 0, imgWidth) , endX = llclamp(posX + llmath::llround(GRID_STEP * mObjectMapTPM), 0, imgWidth - 1); for (; curX <= endX; curX++) { pTextureData[curY * imgWidth + curX] = (fForSale) ? LLColor4U(255, 255, 128, 192).mAll @@ -1199,7 +1199,7 @@ void LLNetMap::renderPropertyLinesForRegion(const LLViewerRegion* pRegion, const { if ( (posY >= 0) && (posY < imgHeight) ) { - S32 curX = llclamp(posX, 0, imgWidth), endX = llclamp(posX + llround(GRID_STEP * mObjectMapTPM), 0, imgWidth - 1); + S32 curX = llclamp(posX, 0, imgWidth), endX = llclamp(posX + llmath::llround(GRID_STEP * mObjectMapTPM), 0, imgWidth - 1); for (; curX <= endX; curX++) pTextureData[posY * imgWidth + curX] = clrOverlay.mAll; } @@ -1208,7 +1208,7 @@ void LLNetMap::renderPropertyLinesForRegion(const LLViewerRegion* pRegion, const { if ( (posX >= 0) && (posX < imgWidth) ) { - S32 curY = llclamp(posY, 0, imgHeight), endY = llclamp(posY + llround(GRID_STEP * mObjectMapTPM), 0, imgHeight - 1); + S32 curY = llclamp(posY, 0, imgHeight), endY = llclamp(posY + llmath::llround(GRID_STEP * mObjectMapTPM), 0, imgHeight - 1); for (; curY <= endY; curY++) pTextureData[curY * imgWidth + posX] = clrOverlay.mAll; } @@ -1227,7 +1227,7 @@ bool LLNetMap::createImage(LLPointer& rawimagep) const // ... which is, the diagonal of the rect. F32 width = (F32)getRect().getWidth(); F32 height = (F32)getRect().getHeight(); - S32 square_size = llround( sqrt(width*width + height*height) ); + S32 square_size = llmath::llround( sqrt(width*width + height*height) ); // Find the least power of two >= the minimum size. const S32 MIN_SIZE = 64; diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index 766cbe50c..b08150b57 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -332,9 +332,9 @@ void LLPanelClassifiedInfo::processProperties(void* data, EAvatarProcessorType t if (!location_text.empty()) location_text.append(", "); - S32 region_x = llround((F32)c_info->pos_global.mdV[VX]) % REGION_WIDTH_UNITS; - S32 region_y = llround((F32)c_info->pos_global.mdV[VY]) % REGION_WIDTH_UNITS; - S32 region_z = llround((F32)c_info->pos_global.mdV[VZ]); + S32 region_x = llmath::llround((F32)c_info->pos_global.mdV[VX]) % REGION_WIDTH_UNITS; + S32 region_y = llmath::llround((F32)c_info->pos_global.mdV[VY]) % REGION_WIDTH_UNITS; + S32 region_z = llmath::llround((F32)c_info->pos_global.mdV[VZ]); std::string buffer = llformat("%s (%d, %d, %d)", c_info->sim_name.c_str(), region_x, region_y, region_z); location_text.append(buffer); @@ -893,9 +893,9 @@ void LLPanelClassifiedInfo::onClickSet() location_text.assign(regionName); location_text.append(", "); - S32 region_x = llround((F32)mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS; - S32 region_y = llround((F32)mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS; - S32 region_z = llround((F32)mPosGlobal.mdV[VZ]); + S32 region_x = llmath::llround((F32)mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS; + S32 region_y = llmath::llround((F32)mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS; + S32 region_z = llmath::llround((F32)mPosGlobal.mdV[VZ]); location_text.append(mSimName); location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z)); diff --git a/indra/newview/llpaneldisplay.cpp b/indra/newview/llpaneldisplay.cpp index 1d4e23ded..19fe7218c 100644 --- a/indra/newview/llpaneldisplay.cpp +++ b/indra/newview/llpaneldisplay.cpp @@ -904,8 +904,8 @@ void LLPanelDisplay::fractionFromDecimal(F32 decimal_val, S32& numerator, S32& d { if (fmodf((decimal_val * test_denominator) + 0.01f, 1.f) < 0.02f) { - numerator = llround(decimal_val * test_denominator); - denominator = llround(test_denominator); + numerator = llmath::llround(decimal_val * test_denominator); + denominator = llmath::llround(test_denominator); break; } } diff --git a/indra/newview/llpanelevent.cpp b/indra/newview/llpanelevent.cpp index ef25b25e9..6b57f1cf9 100644 --- a/indra/newview/llpanelevent.cpp +++ b/indra/newview/llpanelevent.cpp @@ -154,9 +154,9 @@ void LLPanelEvent::processEventInfoReply(LLMessageSystem *msg, void **) F32 global_x = (F32)self->mEventInfo.mPosGlobal.mdV[VX]; F32 global_y = (F32)self->mEventInfo.mPosGlobal.mdV[VY]; - S32 region_x = llround(global_x) % REGION_WIDTH_UNITS; - S32 region_y = llround(global_y) % REGION_WIDTH_UNITS; - S32 region_z = llround((F32)self->mEventInfo.mPosGlobal.mdV[VZ]); + S32 region_x = llmath::llround(global_x) % REGION_WIDTH_UNITS; + S32 region_y = llmath::llround(global_y) % REGION_WIDTH_UNITS; + S32 region_z = llmath::llround((F32)self->mEventInfo.mPosGlobal.mdV[VZ]); std::string desc = self->mEventInfo.mSimName + llformat(" (%d, %d, %d)", region_x, region_y, region_z); self->mTBLocation->setText(desc); diff --git a/indra/newview/llpanelgrouplandmoney.cpp b/indra/newview/llpanelgrouplandmoney.cpp index 8a66eef09..18000914c 100644 --- a/indra/newview/llpanelgrouplandmoney.cpp +++ b/indra/newview/llpanelgrouplandmoney.cpp @@ -462,8 +462,8 @@ void LLPanelGroupLandMoney::impl::processGroupLand(LLMessageSystem* msg) land_type = LLTrans::getString("land_type_unknown"); } - S32 region_x = llround(global_x) % REGION_WIDTH_UNITS; - S32 region_y = llround(global_y) % REGION_WIDTH_UNITS; + S32 region_x = llmath::llround(global_x) % REGION_WIDTH_UNITS; + S32 region_y = llmath::llround(global_y) % REGION_WIDTH_UNITS; std::string location = sim_name + llformat(" (%d, %d)", region_x, region_y); std::string area; committed+=billable_area; diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 71b507ef6..490e22399 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -270,7 +270,7 @@ LLInventoryView::~LLInventoryView( void ) llofstream filtersFile(filterSaveName.str()); if(!LLSDSerialize::toPrettyXML(filterRoot, filtersFile)) { - llwarns << "Could not write to filters save file " << filterSaveName << llendl; + llwarns << "Could not write to filters save file " << filterSaveName.str().c_str() << llendl; } else filtersFile.close(); diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 1cf09178e..c93f41aa6 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -565,9 +565,9 @@ void LLPanelObject::getState( ) LLQuaternion object_rot = objectp->getRotationEdit(); object_rot.getEulerAngles(&(mCurEulerDegrees.mV[VX]), &(mCurEulerDegrees.mV[VY]), &(mCurEulerDegrees.mV[VZ])); mCurEulerDegrees *= RAD_TO_DEG; - mCurEulerDegrees.mV[VX] = fmod(llround(mCurEulerDegrees.mV[VX], OBJECT_ROTATION_PRECISION) + 360.f, 360.f); - mCurEulerDegrees.mV[VY] = fmod(llround(mCurEulerDegrees.mV[VY], OBJECT_ROTATION_PRECISION) + 360.f, 360.f); - mCurEulerDegrees.mV[VZ] = fmod(llround(mCurEulerDegrees.mV[VZ], OBJECT_ROTATION_PRECISION) + 360.f, 360.f); + mCurEulerDegrees.mV[VX] = fmod(llmath::llround(mCurEulerDegrees.mV[VX], OBJECT_ROTATION_PRECISION) + 360.f, 360.f); + mCurEulerDegrees.mV[VY] = fmod(llmath::llround(mCurEulerDegrees.mV[VY], OBJECT_ROTATION_PRECISION) + 360.f, 360.f); + mCurEulerDegrees.mV[VZ] = fmod(llmath::llround(mCurEulerDegrees.mV[VZ], OBJECT_ROTATION_PRECISION) + 360.f, 360.f); if (enable_rotate) { @@ -1965,9 +1965,9 @@ void LLPanelObject::sendRotation(BOOL btn_down) if (mObject.isNull()) return; LLVector3 new_rot(mCtrlRotX->get(), mCtrlRotY->get(), mCtrlRotZ->get()); - new_rot.mV[VX] = llround(new_rot.mV[VX], OBJECT_ROTATION_PRECISION); - new_rot.mV[VY] = llround(new_rot.mV[VY], OBJECT_ROTATION_PRECISION); - new_rot.mV[VZ] = llround(new_rot.mV[VZ], OBJECT_ROTATION_PRECISION); + new_rot.mV[VX] = llmath::llround(new_rot.mV[VX], OBJECT_ROTATION_PRECISION); + new_rot.mV[VY] = llmath::llround(new_rot.mV[VY], OBJECT_ROTATION_PRECISION); + new_rot.mV[VZ] = llmath::llround(new_rot.mV[VZ], OBJECT_ROTATION_PRECISION); // Note: must compare before conversion to radians LLVector3 delta = new_rot - mCurEulerDegrees; diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp index 674d9b65d..c2e2a2cb1 100644 --- a/indra/newview/llpanelpick.cpp +++ b/indra/newview/llpanelpick.cpp @@ -178,9 +178,9 @@ void LLPanelPick::processProperties(void* data, EAvatarProcessorType type) self->mImporting = false; } - S32 region_x = llround((F32)pick_info->pos_global.mdV[VX]) % REGION_WIDTH_UNITS; - S32 region_y = llround((F32)pick_info->pos_global.mdV[VY]) % REGION_WIDTH_UNITS; - S32 region_z = llround((F32)pick_info->pos_global.mdV[VZ]); + S32 region_x = llmath::llround((F32)pick_info->pos_global.mdV[VX]) % REGION_WIDTH_UNITS; + S32 region_y = llmath::llround((F32)pick_info->pos_global.mdV[VY]) % REGION_WIDTH_UNITS; + S32 region_z = llmath::llround((F32)pick_info->pos_global.mdV[VZ]); location_text.append(llformat("(%d, %d, %d)", region_x, region_y, region_z)); mDataReceived = true; @@ -401,9 +401,9 @@ void LLPanelPick::onClickSet() std::string location_text("(will update after save), " + mSimName); - S32 region_x = llround((F32)mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS; - S32 region_y = llround((F32)mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS; - S32 region_z = llround((F32)mPosGlobal.mdV[VZ]); + S32 region_x = llmath::llround((F32)mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS; + S32 region_y = llmath::llround((F32)mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS; + S32 region_z = llmath::llround((F32)mPosGlobal.mdV[VZ]); location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z)); // if sim name in pick is different from current sim name diff --git a/indra/newview/llpanelplace.cpp b/indra/newview/llpanelplace.cpp index 80af2ac56..dfa5d88ef 100644 --- a/indra/newview/llpanelplace.cpp +++ b/indra/newview/llpanelplace.cpp @@ -289,16 +289,16 @@ void LLPanelPlace::processParcelInfo(const LLParcelData& parcel_data) } // Just use given region position for display - S32 region_x = llround(mPosRegion.mV[0]); - S32 region_y = llround(mPosRegion.mV[1]); - S32 region_z = llround(mPosRegion.mV[2]); + S32 region_x = llmath::llround(mPosRegion.mV[0]); + S32 region_y = llmath::llround(mPosRegion.mV[1]); + S32 region_z = llmath::llround(mPosRegion.mV[2]); // If the region position is zero, grab position from the global if(mPosRegion.isExactlyZero()) { - region_x = llround(parcel_data.global_x) % REGION_WIDTH_UNITS; - region_y = llround(parcel_data.global_y) % REGION_WIDTH_UNITS; - region_z = llround(parcel_data.global_z); + region_x = llmath::llround(parcel_data.global_x) % REGION_WIDTH_UNITS; + region_y = llmath::llround(parcel_data.global_y) % REGION_WIDTH_UNITS; + region_z = llmath::llround(parcel_data.global_z); } if(mPosGlobal.isExactlyZero()) diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index 5fa463a34..91372b23c 100644 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -642,12 +642,12 @@ void LLPanelPrimMediaControls::updateShape() // convert screenspace bbox to pixels (in screen coords) LLRect window_rect = gViewerWindow->getWorldViewRectScaled(); LLCoordGL screen_min; - screen_min.mX = llround((F32)window_rect.mLeft + (F32)window_rect.getWidth() * (min.getF32ptr()[VX] + 1.f) * 0.5f); - screen_min.mY = llround((F32)window_rect.mBottom + (F32)window_rect.getHeight() * (min.getF32ptr()[VY] + 1.f) * 0.5f); + screen_min.mX = llmath::llround((F32)window_rect.mLeft + (F32)window_rect.getWidth() * (min.getF32ptr()[VX] + 1.f) * 0.5f); + screen_min.mY = llmath::llround((F32)window_rect.mBottom + (F32)window_rect.getHeight() * (min.getF32ptr()[VY] + 1.f) * 0.5f); LLCoordGL screen_max; - screen_max.mX = llround((F32)window_rect.mLeft + (F32)window_rect.getWidth() * (max.getF32ptr()[VX] + 1.f) * 0.5f); - screen_max.mY = llround((F32)window_rect.mBottom + (F32)window_rect.getHeight() * (max.getF32ptr()[VY] + 1.f) * 0.5f); + screen_max.mX = llmath::llround((F32)window_rect.mLeft + (F32)window_rect.getWidth() * (max.getF32ptr()[VX] + 1.f) * 0.5f); + screen_max.mY = llmath::llround((F32)window_rect.mBottom + (F32)window_rect.getHeight() * (max.getF32ptr()[VY] + 1.f) * 0.5f); // grow panel so that screenspace bounding box fits inside "media_region" element of panel LLRect media_panel_rect; diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp index 6d686b252..4127cfadc 100644 --- a/indra/newview/llpreviewtexture.cpp +++ b/indra/newview/llpreviewtexture.cpp @@ -590,7 +590,7 @@ void LLPreviewTexture::updateDimensions() client_width = getRect().getWidth() - horiz_pad; if (mAspectRatio > 0.f) { - client_height = llround(client_width / mAspectRatio); + client_height = llmath::llround(client_width / mAspectRatio); } else { @@ -608,7 +608,7 @@ void LLPreviewTexture::updateDimensions() if (client_height > max_height) { client_height = max_height; - client_width = llround(client_height * mAspectRatio); + client_width = llmath::llround(client_height * mAspectRatio); } } else diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp index fdd812d65..5862dd958 100644 --- a/indra/newview/llslurl.cpp +++ b/indra/newview/llslurl.cpp @@ -356,9 +356,9 @@ LLSLURL::LLSLURL(const std::string& grid, { mGrid = grid; mRegion = region; - S32 x = llround( (F32)position[VX] ); - S32 y = llround( (F32)position[VY] ); - S32 z = llround( (F32)position[VZ] ); + S32 x = llmath::llround( (F32)position[VX] ); + S32 y = llmath::llround( (F32)position[VY] ); + S32 z = llmath::llround( (F32)position[VZ] ); mType = LOCATION; mPosition = LLVector3(x, y, z); } @@ -417,9 +417,9 @@ std::string LLSLURL::getSLURLString() const case LOCATION: { // lookup the grid - S32 x = llround( (F32)mPosition[VX] ); - S32 y = llround( (F32)mPosition[VY] ); - S32 z = llround( (F32)mPosition[VZ] ); + S32 x = llmath::llround( (F32)mPosition[VX] ); + S32 y = llmath::llround( (F32)mPosition[VY] ); + S32 z = llmath::llround( (F32)mPosition[VZ] ); //return LLGridManager::getInstance()->getSLURLBase(mGrid) + //Singu TODO: Implement LLHippoGridMgr::getSLURLBase some day. For now it's hardcoded. std::string fixed_slurl; @@ -471,9 +471,9 @@ std::string LLSLURL::getLoginString() const case LOCATION: unescaped_start << "uri:" << mRegion << "&" - << llround(mPosition[0]) << "&" - << llround(mPosition[1]) << "&" - << llround(mPosition[2]); + << llmath::llround(mPosition[0]) << "&" + << llmath::llround(mPosition[1]) << "&" + << llmath::llround(mPosition[2]); break; case HOME_LOCATION: unescaped_start << "home"; @@ -517,9 +517,9 @@ std::string LLSLURL::getLocationString() const { return llformat("%s/%d/%d/%d", mRegion.c_str(), - (int)llround(mPosition[0]), - (int)llround(mPosition[1]), - (int)llround(mPosition[2])); + (int)llmath::llround(mPosition[0]), + (int)llmath::llround(mPosition[1]), + (int)llmath::llround(mPosition[2])); } // static diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index dc1e19f1f..a6c8009ac 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -1616,7 +1616,7 @@ bool idle_startup() if (gNoRender) { LL_WARNS("AppInit") << "Bad login - missing return values" << LL_ENDL; - LL_WARNS("AppInit") << emsg << LL_ENDL; + LL_WARNS("AppInit") << emsg.str() << LL_ENDL; exit(0); } // Bounce back to the login screen. @@ -1632,7 +1632,7 @@ bool idle_startup() if (gNoRender) { LL_WARNS("AppInit") << "Failed to login!" << LL_ENDL; - LL_WARNS("AppInit") << emsg << LL_ENDL; + LL_WARNS("AppInit") << emsg.str() << LL_ENDL; exit(0); } // Bounce back to the login screen. diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp index fd7c0da59..bdf34d6c6 100644 --- a/indra/newview/llsurface.cpp +++ b/indra/newview/llsurface.cpp @@ -1425,10 +1425,10 @@ BOOL LLSurface::generateWaterTexture(const F32 x, const F32 y, S32 x_begin, y_begin, x_end, y_end; - x_begin = llround(x * scale_inv); - y_begin = llround(y * scale_inv); - x_end = llround((x + width) * scale_inv); - y_end = llround((y + width) * scale_inv); + x_begin = llmath::llround(x * scale_inv); + y_begin = llmath::llround(y * scale_inv); + x_end = llmath::llround((x + width) * scale_inv); + y_end = llmath::llround((y + width) * scale_inv); if (x_end > tex_width) { @@ -1476,9 +1476,9 @@ BOOL LLSurface::generateWaterTexture(const F32 x, const F32 y, // Want non-linear curve for transparency gradient coloru = MAX_WATER_COLOR; const F32 frac = 1.f - 2.f/(2.f - (height - WATER_HEIGHT)); - S32 alpha = 64 + llround((255-64)*frac); + S32 alpha = 64 + llmath::llround((255-64)*frac); - alpha = llmin(llround((F32)MAX_WATER_COLOR.mV[3]), alpha); + alpha = llmin(llmath::llround((F32)MAX_WATER_COLOR.mV[3]), alpha); alpha = llmax(64, alpha); coloru.mV[3] = alpha; diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 7a0955c4f..fca81e83f 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -1167,7 +1167,7 @@ LLTextureCtrl::LLTextureCtrl( S32 image_top = getRect().getHeight(); S32 image_bottom = BTN_HEIGHT_SMALL; S32 image_middle = (image_top + image_bottom) / 2; - S32 line_height = llround(LLFontGL::getFontSansSerifSmall()->getLineHeight()); + S32 line_height = llmath::llround(LLFontGL::getFontSansSerifSmall()->getLineHeight()); mTentativeLabel = new LLTextBox( std::string("Multiple"), LLRect( diff --git a/indra/newview/lltoolselectland.cpp b/indra/newview/lltoolselectland.cpp index 0c6637828..9a09c3ed3 100644 --- a/indra/newview/lltoolselectland.cpp +++ b/indra/newview/lltoolselectland.cpp @@ -226,8 +226,8 @@ void LLToolSelectLand::handleDeselect() void LLToolSelectLand::roundXY(LLVector3d &vec) { - vec.mdV[VX] = llround( vec.mdV[VX], (F64)PARCEL_GRID_STEP_METERS ); - vec.mdV[VY] = llround( vec.mdV[VY], (F64)PARCEL_GRID_STEP_METERS ); + vec.mdV[VX] = llmath::llround( vec.mdV[VX], (F64)PARCEL_GRID_STEP_METERS ); + vec.mdV[VY] = llmath::llround( vec.mdV[VY], (F64)PARCEL_GRID_STEP_METERS ); } diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 4994869ce..6fe2d4d9d 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -1254,8 +1254,8 @@ LLRect get_whole_screen_region() if (zoom_factor > 1.f) { S32 num_horizontal_tiles = llceil(zoom_factor); - S32 tile_width = llround((F32)gViewerWindow->getWorldViewWidthScaled() / zoom_factor); - S32 tile_height = llround((F32)gViewerWindow->getWorldViewHeightScaled() / zoom_factor); + S32 tile_width = llmath::llround((F32)gViewerWindow->getWorldViewWidthScaled() / zoom_factor); + S32 tile_height = llmath::llround((F32)gViewerWindow->getWorldViewHeightScaled() / zoom_factor); int tile_y = sub_region / num_horizontal_tiles; int tile_x = sub_region - (tile_y * num_horizontal_tiles); @@ -1539,8 +1539,8 @@ void render_ui_2d() int pos_y = sub_region / llceil(zoom_factor); int pos_x = sub_region - (pos_y*llceil(zoom_factor)); // offset for this tile - LLFontGL::sCurOrigin.mX -= llround((F32)gViewerWindow->getWindowWidthScaled() * (F32)pos_x / zoom_factor); - LLFontGL::sCurOrigin.mY -= llround((F32)gViewerWindow->getWindowHeightScaled() * (F32)pos_y / zoom_factor); + LLFontGL::sCurOrigin.mX -= llmath::llround((F32)gViewerWindow->getWindowWidthScaled() * (F32)pos_x / zoom_factor); + LLFontGL::sCurOrigin.mY -= llmath::llround((F32)gViewerWindow->getWindowHeightScaled() * (F32)pos_y / zoom_factor); } stop_glerror(); diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp index 5104f7972..4bd4b4c42 100644 --- a/indra/newview/llviewerkeyboard.cpp +++ b/indra/newview/llviewerkeyboard.cpp @@ -65,7 +65,7 @@ void agent_jump( EKeystate s ) { if( KEYSTATE_UP == s ) return; F32 time = gKeyboard->getCurKeyElapsedTime(); - S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount()); + S32 frame_count = llmath::llround(gKeyboard->getCurKeyElapsedFrameCount()); if( time < FLY_TIME || frame_count <= FLY_FRAMES || gAgent.upGrabbed() @@ -141,7 +141,7 @@ static void agent_push_forwardbackward( EKeystate s, S32 direction, LLAgent::EDo if (KEYSTATE_UP == s) return; F32 time = gKeyboard->getCurKeyElapsedTime(); - S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount()); + S32 frame_count = llmath::llround(gKeyboard->getCurKeyElapsedFrameCount()); if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES) { @@ -169,7 +169,7 @@ static void agent_slide_leftright( EKeystate s, S32 direction, LLAgent::EDoubleT agent_handle_doubletap_run(s, mode); if( KEYSTATE_UP == s ) return; F32 time = gKeyboard->getCurKeyElapsedTime(); - S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount()); + S32 frame_count = llmath::llround(gKeyboard->getCurKeyElapsedFrameCount()); if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES) { diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 2050c3983..5a876559f 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -940,7 +940,7 @@ void LLViewerMedia::updateMedia(void *dummy_arg) { F32 approximate_interest_dimension = (F32) sqrt(pimpl->getInterest()); - pimpl->setLowPrioritySizeLimit(llround(approximate_interest_dimension)); + pimpl->setLowPrioritySizeLimit(llmath::llround(approximate_interest_dimension)); } } else @@ -2366,8 +2366,8 @@ void LLViewerMediaImpl::scaleTextureCoords(const LLVector2& texture_coords, S32 texture_y = 1.0 + texture_y; // scale x and y to texel units. - *x = llround(texture_x * plugin->getTextureWidth()); - *y = llround((1.0f - texture_y) * plugin->getTextureHeight()); + *x = llmath::llround(texture_x * plugin->getTextureWidth()); + *y = llmath::llround((1.0f - texture_y) * plugin->getTextureHeight()); // Adjust for the difference between the actual texture height and the amount of the texture in use. *y -= (plugin->getTextureHeight() - plugin->getHeight()); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 1c5ed208f..cdac1f226 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3958,9 +3958,9 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) { // Compute the object SLURL. LLVector3 pos = chatter->getPositionRegion(); - S32 x = llround((F32)fmod((F64)pos.mV[VX], (F64)REGION_WIDTH_METERS)); - S32 y = llround((F32)fmod((F64)pos.mV[VY], (F64)REGION_WIDTH_METERS)); - S32 z = llround((F32)pos.mV[VZ]); + S32 x = llmath::llround((F32)fmod((F64)pos.mV[VX], (F64)REGION_WIDTH_METERS)); + S32 y = llmath::llround((F32)fmod((F64)pos.mV[VY], (F64)REGION_WIDTH_METERS)); + S32 z = llmath::llround((F32)pos.mV[VZ]); std::ostringstream location; location << chatter->getRegion()->getName() << "/" << x << "/" << y << "/" << z; query_string["slurl"] = location.str(); diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 977d8fede..63ba67994 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -315,7 +315,7 @@ S32 LLViewerParcelMgr::getSelectedArea() const F64 width = mEastNorth.mdV[VX] - mWestSouth.mdV[VX]; F64 height = mEastNorth.mdV[VY] - mWestSouth.mdV[VY]; F32 area = (F32)(width * height); - rv = llround(area); + rv = llmath::llround(area); } return rv; } @@ -335,10 +335,10 @@ void LLViewerParcelMgr::writeHighlightSegments(F32 west, F32 south, F32 east, F32 north) { S32 x, y; - S32 min_x = llround( west / PARCEL_GRID_STEP_METERS ); - S32 max_x = llround( east / PARCEL_GRID_STEP_METERS ); - S32 min_y = llround( south / PARCEL_GRID_STEP_METERS ); - S32 max_y = llround( north / PARCEL_GRID_STEP_METERS ); + S32 min_x = llmath::llround( west / PARCEL_GRID_STEP_METERS ); + S32 max_x = llmath::llround( east / PARCEL_GRID_STEP_METERS ); + S32 min_y = llmath::llround( south / PARCEL_GRID_STEP_METERS ); + S32 max_y = llmath::llround( north / PARCEL_GRID_STEP_METERS ); const S32 STRIDE = mParcelsPerEdge+1; @@ -450,12 +450,12 @@ LLParcelSelectionHandle LLViewerParcelMgr::selectParcelAt(const LLVector3d& pos_ LLVector3d northeast = pos_global; southwest -= LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 ); - southwest.mdV[VX] = llround( southwest.mdV[VX], (F64)PARCEL_GRID_STEP_METERS ); - southwest.mdV[VY] = llround( southwest.mdV[VY], (F64)PARCEL_GRID_STEP_METERS ); + southwest.mdV[VX] = llmath::llround( southwest.mdV[VX], (F64)PARCEL_GRID_STEP_METERS ); + southwest.mdV[VY] = llmath::llround( southwest.mdV[VY], (F64)PARCEL_GRID_STEP_METERS ); northeast += LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 ); - northeast.mdV[VX] = llround( northeast.mdV[VX], (F64)PARCEL_GRID_STEP_METERS ); - northeast.mdV[VY] = llround( northeast.mdV[VY], (F64)PARCEL_GRID_STEP_METERS ); + northeast.mdV[VX] = llmath::llround( northeast.mdV[VX], (F64)PARCEL_GRID_STEP_METERS ); + northeast.mdV[VY] = llmath::llround( northeast.mdV[VY], (F64)PARCEL_GRID_STEP_METERS ); // Snap to parcel return selectLand( southwest, northeast, TRUE ); diff --git a/indra/newview/llviewerpartsim.cpp b/indra/newview/llviewerpartsim.cpp index 3c0c37b3e..5e334e634 100644 --- a/indra/newview/llviewerpartsim.cpp +++ b/indra/newview/llviewerpartsim.cpp @@ -397,7 +397,7 @@ void LLViewerPartGroup::updateParticles(const F32 lastdt) } // Do glow interpolation - part->mGlow.mV[3] = (U8) llround(lerp(part->mStartGlow, part->mEndGlow, frac)*255.f); + part->mGlow.mV[3] = (U8) llmath::llround(lerp(part->mStartGlow, part->mEndGlow, frac)*255.f); // Set the last update time to now. part->mLastUpdateTime = cur_time; diff --git a/indra/newview/llviewerpartsource.cpp b/indra/newview/llviewerpartsource.cpp index cbd0cc31e..10b70f398 100644 --- a/indra/newview/llviewerpartsource.cpp +++ b/indra/newview/llviewerpartsource.cpp @@ -322,7 +322,7 @@ void LLViewerPartSourceScript::update(const F32 dt) part->mStartGlow = mPartSysData.mPartData.mStartGlow; part->mEndGlow = mPartSysData.mPartData.mEndGlow; - part->mGlow = LLColor4U(0, 0, 0, (U8) llround(part->mStartGlow*255.f)); + part->mGlow = LLColor4U(0, 0, 0, (U8) llmath::llround(part->mStartGlow*255.f)); if (mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_DROP) { diff --git a/indra/newview/llviewertextureanim.cpp b/indra/newview/llviewertextureanim.cpp index 1e6b7a343..fc5d30890 100644 --- a/indra/newview/llviewertextureanim.cpp +++ b/indra/newview/llviewertextureanim.cpp @@ -174,7 +174,7 @@ S32 LLViewerTextureAnim::animateTextures(F32 &off_s, F32 &off_t, if (!(mMode & SMOOTH)) { - frame_counter = (F32)llround(frame_counter); + frame_counter = (F32)llmath::llround(frame_counter); } // diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 099927c6d..8663e82b7 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -846,8 +846,8 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK BOOL handled = FALSE; S32 x = pos.mX; S32 y = pos.mY; - x = llround((F32)x / mDisplayScale.mV[VX]); - y = llround((F32)y / mDisplayScale.mV[VY]); + x = llmath::llround((F32)x / mDisplayScale.mV[VX]); + y = llmath::llround((F32)y / mDisplayScale.mV[VY]); if (down) { @@ -1021,8 +1021,8 @@ BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK { S32 x = pos.mX; S32 y = pos.mY; - x = llround((F32)x / mDisplayScale.mV[VX]); - y = llround((F32)y / mDisplayScale.mV[VY]); + x = llmath::llround((F32)x / mDisplayScale.mV[VX]); + y = llmath::llround((F32)y / mDisplayScale.mV[VY]); LLView::sMouseHandlerMessage.clear(); @@ -1220,8 +1220,8 @@ void LLViewerWindow::handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask S32 x = pos.mX; S32 y = pos.mY; - x = llround((F32)x / mDisplayScale.mV[VX]); - y = llround((F32)y / mDisplayScale.mV[VY]); + x = llmath::llround((F32)x / mDisplayScale.mV[VX]); + y = llmath::llround((F32)y / mDisplayScale.mV[VY]); mMouseInWindow = TRUE; @@ -1709,7 +1709,7 @@ LLViewerWindow::LLViewerWindow( LLCoordWindow size; mWindow->getSize(&size); mWindowRectRaw.set(0, size.mY, size.mX, 0); - mWindowRectScaled.set(0, llround((F32)size.mY / mDisplayScale.mV[VY]), llround((F32)size.mX / mDisplayScale.mV[VX]), 0); + mWindowRectScaled.set(0, llmath::llround((F32)size.mY / mDisplayScale.mV[VY]), llmath::llround((F32)size.mX / mDisplayScale.mV[VX]), 0); } LLFontManager::initClass(); @@ -2391,8 +2391,8 @@ void LLViewerWindow::reshape(S32 width, S32 height) LLUI::setScaleFactor(mDisplayScale); // update our window rectangle - mWindowRectScaled.mRight = mWindowRectScaled.mLeft + llround((F32)width / mDisplayScale.mV[VX]); - mWindowRectScaled.mTop = mWindowRectScaled.mBottom + llround((F32)height / mDisplayScale.mV[VY]); + mWindowRectScaled.mRight = mWindowRectScaled.mLeft + llmath::llround((F32)width / mDisplayScale.mV[VX]); + mWindowRectScaled.mTop = mWindowRectScaled.mBottom + llmath::llround((F32)height / mDisplayScale.mV[VY]); setup2DViewport(); @@ -2552,8 +2552,8 @@ void LLViewerWindow::draw() microsecondsToTimecodeString(gFrameTime,text); const LLFontGL* font = LLFontGL::getFontSansSerif(); font->renderUTF8(text, 0, - llround((getWindowWidthScaled()/2)-100.f), - llround((getWindowHeightScaled()-60.f)), + llmath::llround((getWindowWidthScaled()/2)-100.f), + llmath::llround((getWindowHeightScaled()-60.f)), LLColor4( 1.f, 1.f, 1.f, 1.f ), LLFontGL::LEFT, LLFontGL::TOP); } @@ -2653,7 +2653,7 @@ void LLViewerWindow::draw() const S32 DIST_FROM_TOP = 20; LLFontGL::getFontSansSerifBig()->renderUTF8( mOverlayTitle, 0, - llround( getWindowWidthScaled() * 0.5f), + llmath::llround( getWindowWidthScaled() * 0.5f), getWindowHeightScaled() - DIST_FROM_TOP, LLColor4(1, 1, 1, 0.4f), LLFontGL::HCENTER, LLFontGL::TOP); @@ -3505,7 +3505,7 @@ void LLViewerWindow::updateMouseDelta() fdx = fdx + ((F32) dx - fdx) * llmin(gFrameIntervalSeconds*amount,1.f); fdy = fdy + ((F32) dy - fdy) * llmin(gFrameIntervalSeconds*amount,1.f); - mCurrentMouseDelta.set(llround(fdx), llround(fdy)); + mCurrentMouseDelta.set(llmath::llround(fdx), llmath::llround(fdy)); mouse_vel.setVec(fdx,fdy); } else @@ -4788,10 +4788,10 @@ bool LLViewerWindow::rawRawSnapshot(LLImageRaw *raw, // However, if the buffer turns out to be too large, then clamp it to max_size. scale_factor = llmin(max_size / snapshot_width, max_size / snapshot_height)) // Clamp { - image_buffer_x = llround(unscaled_image_buffer_x * scale_factor); - image_buffer_y = llround(unscaled_image_buffer_y * scale_factor); - S32 image_size_x = llround(snapshot_width * scale_factor); - S32 image_size_y = llround(snapshot_width * scale_factor); + image_buffer_x = llmath::llround(unscaled_image_buffer_x * scale_factor); + image_buffer_y = llmath::llround(unscaled_image_buffer_y * scale_factor); + S32 image_size_x = llmath::llround(snapshot_width * scale_factor); + S32 image_size_y = llmath::llround(snapshot_width * scale_factor); if (llmax(image_size_x, image_size_y) > max_size && // Boundary check to avoid memory overflow. internal_scale <= 1.f && !reset_deferred) // SHY_MOD: If supersampling... Don't care about max_size. { @@ -5058,7 +5058,7 @@ void LLViewerWindow::drawMouselookInstructions() INSTRUCTIONS_PAD, getWindowHeight() - INSTRUCTIONS_PAD, font->getWidth( instructions ) + 2 * INSTRUCTIONS_PAD, - llround(font->getLineHeight() + 2 * INSTRUCTIONS_PAD)); + llmath::llround(font->getLineHeight() + 2 * INSTRUCTIONS_PAD)); { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); @@ -5668,8 +5668,8 @@ void LLViewerWindow::calcDisplayScale() if (mWindow->getFullscreen()) { - display_scale.mV[0] = llround(display_scale.mV[0], 2.0f/(F32) mWindowRectRaw.getWidth()); - display_scale.mV[1] = llround(display_scale.mV[1], 2.0f/(F32) mWindowRectRaw.getHeight()); + display_scale.mV[0] = llmath::llround(display_scale.mV[0], 2.0f/(F32) mWindowRectRaw.getWidth()); + display_scale.mV[1] = llmath::llround(display_scale.mV[1], 2.0f/(F32) mWindowRectRaw.getHeight()); } if (display_scale != mDisplayScale) @@ -5953,8 +5953,8 @@ void LLPickInfo::updateXYCoords() LLPointer imagep = LLViewerTextureManager::getFetchedTexture(tep->getID()); if(mUVCoords.mV[VX] >= 0.f && mUVCoords.mV[VY] >= 0.f && imagep.notNull()) { - mXYCoords.mX = llround(mUVCoords.mV[VX] * (F32)imagep->getWidth()); - mXYCoords.mY = llround((1.f - mUVCoords.mV[VY]) * (F32)imagep->getHeight()); + mXYCoords.mX = llmath::llround(mUVCoords.mV[VX] * (F32)imagep->getWidth()); + mXYCoords.mY = llmath::llround((1.f - mUVCoords.mV[VY]) * (F32)imagep->getHeight()); } } } @@ -5983,7 +5983,7 @@ void LLPickInfo::getSurfaceInfo() if (objectp) { - if (gViewerWindow->cursorIntersect(llround((F32)mMousePt.mX), llround((F32)mMousePt.mY), 1024.f, + if (gViewerWindow->cursorIntersect(llmath::llround((F32)mMousePt.mX), llmath::llround((F32)mMousePt.mY), 1024.f, objectp, -1, mPickTransparent, &mObjectFace, &intersection, @@ -6041,8 +6041,8 @@ LLVector2 LLPickInfo::pickUV() objectp->mDrawable.notNull() && objectp->getPCode() == LL_PCODE_VOLUME && mObjectFace < objectp->mDrawable->getNumFaces()) { - S32 scaled_x = llround((F32)mPickPt.mX * gViewerWindow->getDisplayScale().mV[VX]); - S32 scaled_y = llround((F32)mPickPt.mY * gViewerWindow->getDisplayScale().mV[VY]); + S32 scaled_x = llmath::llround((F32)mPickPt.mX * gViewerWindow->getDisplayScale().mV[VX]); + S32 scaled_y = llmath::llround((F32)mPickPt.mY * gViewerWindow->getDisplayScale().mV[VY]); const S32 UV_PICK_WIDTH = 5; const S32 UV_PICK_HALF_WIDTH = (UV_PICK_WIDTH - 1) / 2; U8 uv_pick_buffer[UV_PICK_WIDTH * UV_PICK_WIDTH * 4]; diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp index 94ae918be..a15fb9872 100644 --- a/indra/newview/llvlcomposition.cpp +++ b/indra/newview/llvlcomposition.cpp @@ -133,10 +133,10 @@ BOOL LLVLComposition::generateHeights(const F32 x, const F32 y, S32 x_begin, y_begin, x_end, y_end; - x_begin = llround( x * mScaleInv ); - y_begin = llround( y * mScaleInv ); - x_end = llround( (x + width) * mScaleInv ); - y_end = llround( (y + width) * mScaleInv ); + x_begin = llmath::llround( x * mScaleInv ); + y_begin = llmath::llround( y * mScaleInv ); + x_end = llmath::llround( (x + width) * mScaleInv ); + y_end = llmath::llround( (y + width) * mScaleInv ); if (x_end > mWidth) { @@ -331,8 +331,8 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y, S32 x_begin, y_begin, x_end, y_end; x_begin = (S32)(x * mScaleInv); y_begin = (S32)(y * mScaleInv); - x_end = llround( (x + width) * mScaleInv ); - y_end = llround( (y + width) * mScaleInv ); + x_end = llmath::llround( (x + width) * mScaleInv ); + y_end = llmath::llround( (y + width) * mScaleInv ); if (x_end > mWidth) { diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 802105235..2e5e1d7e9 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -7995,7 +7995,7 @@ void LLVOAvatar::parseAppearanceMessage(LLMessageSystem* mesgsys, LLAppearanceMe if (it != contents.mParams.end()) { S32 index = it - contents.mParams.begin(); - contents.mParamAppearanceVersion = llround(contents.mParamWeights[index]); + contents.mParamAppearanceVersion = llmath::llround(contents.mParamWeights[index]); LL_DEBUGS("Avatar") << "appversion req by appearance_version param: " << contents.mParamAppearanceVersion << LL_ENDL; } } diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 46edb4fa7..fadf0ee51 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -2597,7 +2597,7 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void) if(!p->mIsSelf) { // scale from the range 0.0-1.0 to vivox volume in the range 0-100 - S32 volume = llround(p->mVolume / VOLUME_SCALE_VIVOX); + S32 volume = llmath::llround(p->mVolume / VOLUME_SCALE_VIVOX); bool mute = p->mOnMuteList; if(mute) diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp index 7b4077798..837d0aea2 100644 --- a/indra/newview/llvopartgroup.cpp +++ b/indra/newview/llvopartgroup.cpp @@ -699,7 +699,7 @@ void LLVOPartGroup::getGeometry(S32 idx, } else { - pglow = LLColor4U(0, 0, 0, (U8) llround(255.f*part.mStartGlow)); + pglow = LLColor4U(0, 0, 0, (U8) llmath::llround(255.f*part.mStartGlow)); pcolor = part.mStartColor; } } diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 7010d8c7f..d754b5426 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -1210,7 +1210,7 @@ S32 LLVOVolume::computeLODDetail(F32 distance, F32 radius) { // We've got LOD in the profile, and in the twist. Use radius. F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance; - cur_detail = LLVolumeLODGroup::getDetailFromTan(llround(tan_angle, 0.01f)); + cur_detail = LLVolumeLODGroup::getDetailFromTan(llmath::llround(tan_angle, 0.01f)); } else { @@ -1257,8 +1257,8 @@ BOOL LLVOVolume::calcLOD() // DON'T Compensate for field of view changing on FOV zoom. distance *= F_PI/3.f; - cur_detail = computeLODDetail(llround(distance, 0.01f), - llround(radius, 0.01f)); + cur_detail = computeLODDetail(llmath::llround(distance, 0.01f), + llmath::llround(radius, 0.01f)); if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_LOD_INFO) && @@ -1271,7 +1271,7 @@ BOOL LLVOVolume::calcLOD() if (cur_detail != mLOD) { - mAppAngle = llround((F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG, 0.01f); + mAppAngle = llmath::llround((F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG, 0.01f); mLOD = cur_detail; return TRUE; } @@ -4903,7 +4903,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) } } /*LLColor4 clr = facep->getFaceColor(); - LLColor4U clru = LLColor4U(llround(clr.mV[0] * 255.f), llround(clr.mV[0] * 255.f), llround(clr.mV[0] * 255.f), llround(clr.mV[0] * 255.f)); + LLColor4U clru = LLColor4U(llmath::llround(clr.mV[0] * 255.f), llmath::llround(clr.mV[0] * 255.f), llmath::llround(clr.mV[0] * 255.f), llmath::llround(clr.mV[0] * 255.f)); if(clru.mV[0] == 164 && clru.mV[1] == 106 && clr.mV[2] == 65) { llinfos << "Facepool = " << type << " alpha = " << clr.mV[3] << llendl; diff --git a/indra/newview/llwlanimator.cpp b/indra/newview/llwlanimator.cpp index 953898fe6..3bdc1ae5e 100644 --- a/indra/newview/llwlanimator.cpp +++ b/indra/newview/llwlanimator.cpp @@ -267,7 +267,7 @@ std::string LLWLAnimator::timeToString(F32 curTime) // get hours and minutes hours = (S32) (24.0 * curTime); curTime -= ((F32) hours / 24.0f); - min = llround(24.0f * 60.0f * curTime); + min = llmath::llround(24.0f * 60.0f * curTime); // handle case where it's 60 if(min == 60) diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 8e1b1e4be..ee1a52b9f 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -1122,7 +1122,7 @@ void LLWorld::updateWaterObjects() // Count the found coastline. F32 new_water_height = water_heights[index]; LL_DEBUGS("WaterHeight") << " This is void; counting coastline with water height of " << new_water_height << LL_ENDL; - S32 new_water_height_cm = llround(new_water_height * 100); + S32 new_water_height_cm = llmath::llround(new_water_height * 100); int count = (water_height_counts[new_water_height_cm] += 1); // Just use the lowest water height: this is mainly about the horizon water, // and whatever we do, we don't want it to be possible to look under the water diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index 2d76879e2..3742ba4df 100644 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -467,9 +467,9 @@ void LLWorldMapView::draw() if (overlayimage) { // Inform the fetch mechanism of the size we need - S32 draw_size = llround(sMapScale); - overlayimage->setKnownDrawSize( llround(draw_size * LLUI::getScaleFactor().mV[VX] * ((F32)info->getSizeX() / REGION_WIDTH_METERS)), - llround(draw_size * LLUI::getScaleFactor().mV[VY] * ((F32)info->getSizeY() / REGION_WIDTH_METERS))); + S32 draw_size = llmath::llround(sMapScale); + overlayimage->setKnownDrawSize( llmath::llround(draw_size * LLUI::getScaleFactor().mV[VX] * ((F32)info->getSizeX() / REGION_WIDTH_METERS)), + llmath::llround(draw_size * LLUI::getScaleFactor().mV[VY] * ((F32)info->getSizeY() / REGION_WIDTH_METERS))); // Draw something whenever we have enough info if (overlayimage->hasGLTexture() && !overlayimage->isMissingAsset() && overlayimage->getID() != IMG_DEFAULT) { @@ -674,14 +674,14 @@ void LLWorldMapView::draw() drawImage(pos_global, sAvatarYouImage); LLVector3 pos_map = globalPosToView(pos_global); - if (!pointInView(llround(pos_map.mV[VX]), llround(pos_map.mV[VY]))) + if (!pointInView(llmath::llround(pos_map.mV[VX]), llmath::llround(pos_map.mV[VY]))) { drawTracking(pos_global, lerp(LLColor4::yellow, LLColor4::orange, 0.4f), TRUE, "You are here", "", - llround(LLFontGL::getFontSansSerifSmall()->getLineHeight())); // offset vertically by one line, to avoid overlap with target tracking + llmath::llround(LLFontGL::getFontSansSerifSmall()->getLineHeight())); // offset vertically by one line, to avoid overlap with target tracking } // Show your viewing angle @@ -823,8 +823,8 @@ void LLWorldMapView::drawLegacyBackgroundLayers(S32 width, S32 height) { } current_image->setBoostLevel(LLGLTexture::BOOST_MAP); - current_image->setKnownDrawSize(llround(pix_width * LLUI::getScaleFactor().mV[VX]), - llround(pix_height * LLUI::getScaleFactor().mV[VY])); + current_image->setKnownDrawSize(llmath::llround(pix_width * LLUI::getScaleFactor().mV[VX]), + llmath::llround(pix_height * LLUI::getScaleFactor().mV[VY])); if (!current_image->hasGLTexture()) //Still loading. { @@ -911,9 +911,9 @@ F32 LLWorldMapView::drawLegacySimTile(LLSimInfo& sim_info, S32 left, S32 top, S3 //call setKnownDrawSize if image is still loading, or its actually being drawn. if(sim_fetching || alpha >= ALPHA_CUTOFF) { - S32 draw_size = llround(sMapScale); - simimage->setKnownDrawSize( llround(draw_size * LLUI::getScaleFactor().mV[VX] * ((F32)sim_info.getSizeX() / REGION_WIDTH_METERS)), - llround(draw_size * LLUI::getScaleFactor().mV[VY] * ((F32)sim_info.getSizeY() / REGION_WIDTH_METERS))); + S32 draw_size = llmath::llround(sMapScale); + simimage->setKnownDrawSize( llmath::llround(draw_size * LLUI::getScaleFactor().mV[VX] * ((F32)sim_info.getSizeX() / REGION_WIDTH_METERS)), + llmath::llround(draw_size * LLUI::getScaleFactor().mV[VY] * ((F32)sim_info.getSizeY() / REGION_WIDTH_METERS))); simimage->setBoostLevel(LLGLTexture::BOOST_MAP); if(alpha >= ALPHA_CUTOFF) { @@ -1128,8 +1128,8 @@ void LLWorldMapView::drawGenericItem(const LLItemInfo& item, LLUIImagePtr image) void LLWorldMapView::drawImage(const LLVector3d& global_pos, LLUIImagePtr image, const LLColor4& color) { LLVector3 pos_map = globalPosToView( global_pos ); - image->draw(llround(pos_map.mV[VX] - image->getWidth() /2.f), - llround(pos_map.mV[VY] - image->getHeight()/2.f), + image->draw(llmath::llround(pos_map.mV[VX] - image->getWidth() /2.f), + llmath::llround(pos_map.mV[VY] - image->getHeight()/2.f), color); } @@ -1138,8 +1138,8 @@ void LLWorldMapView::drawImageStack(const LLVector3d& global_pos, LLUIImagePtr i LLVector3 pos_map = globalPosToView( global_pos ); for(U32 i=0; idraw(llround(pos_map.mV[VX] - image->getWidth() /2.f), - llround(pos_map.mV[VY] - image->getHeight()/2.f + i*offset), + image->draw(llmath::llround(pos_map.mV[VX] - image->getWidth() /2.f), + llmath::llround(pos_map.mV[VY] - image->getHeight()/2.f + i*offset), color); } } @@ -1323,8 +1323,8 @@ void LLWorldMapView::drawTracking(const LLVector3d& pos_global, const LLColor4& const std::string& label, const std::string& tooltip, S32 vert_offset ) { LLVector3 pos_local = globalPosToView( pos_global ); - S32 x = llround( pos_local.mV[VX] ); - S32 y = llround( pos_local.mV[VY] ); + S32 x = llmath::llround( pos_local.mV[VX] ); + S32 y = llmath::llround( pos_local.mV[VY] ); LLFontGL* font = LLFontGL::getFontSansSerifSmall(); S32 text_x = x; S32 text_y = (S32)(y - sTrackCircleImage->getHeight()/2 - font->getLineHeight()); @@ -1356,7 +1356,7 @@ void LLWorldMapView::drawTracking(const LLVector3d& pos_global, const LLColor4& const S32 TEXT_PADDING = DEFAULT_TRACKING_ARROW_SIZE + 2; S32 half_text_width = llfloor(font->getWidthF32(label) * 0.5f); text_x = llclamp(text_x, half_text_width + TEXT_PADDING, getRect().getWidth() - half_text_width - TEXT_PADDING); - text_y = llclamp(text_y + vert_offset, TEXT_PADDING + vert_offset, getRect().getHeight() - llround(font->getLineHeight()) - TEXT_PADDING - vert_offset); + text_y = llclamp(text_y + vert_offset, TEXT_PADDING + vert_offset, getRect().getHeight() - llmath::llround(font->getLineHeight()) - TEXT_PADDING - vert_offset); //if (label != "") // [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.4.5) | Added: RLVa-1.0.0 @@ -1484,8 +1484,8 @@ static void drawDot(F32 x_pixels, F32 y_pixels, if(-HEIGHT_THRESHOLD <= relative_z && relative_z <= HEIGHT_THRESHOLD) { - dot_image->draw(llround(x_pixels) - dot_image->getWidth()/2, - llround(y_pixels) - dot_image->getHeight()/2, + dot_image->draw(llmath::llround(x_pixels) - dot_image->getWidth()/2, + llmath::llround(y_pixels) - dot_image->getHeight()/2, color); } else @@ -1540,9 +1540,9 @@ void LLWorldMapView::drawAvatar(F32 x_pixels, dot_image = sAvatarAboveImage; } - S32 dot_width = llround(dot_radius * 2.f); - dot_image->draw(llround(x_pixels - dot_radius), - llround(y_pixels - dot_radius), + S32 dot_width = llmath::llround(dot_radius * 2.f); + dot_image->draw(llmath::llround(x_pixels - dot_radius), + llmath::llround(y_pixels - dot_radius), dot_width, dot_width, color); @@ -1569,8 +1569,8 @@ void LLWorldMapView::drawIconName(F32 x_pixels, const std::string& second_line) { const S32 VERT_PAD = 8; - S32 text_x = llround(x_pixels); - S32 text_y = llround(y_pixels + S32 text_x = llmath::llround(x_pixels); + S32 text_y = llmath::llround(y_pixels - BIG_DOT_RADIUS - VERT_PAD); @@ -1584,7 +1584,7 @@ void LLWorldMapView::drawIconName(F32 x_pixels, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW); - text_y -= llround(LLFontGL::getFontSansSerif()->getLineHeight()); + text_y -= llmath::llround(LLFontGL::getFontSansSerif()->getLineHeight()); // render text LLFontGL::getFontSansSerif()->renderUTF8(second_line, 0, @@ -1758,8 +1758,8 @@ void LLWorldMapView::setDirectionPos( LLTextBox* text_box, F32 rotation ) F32 radius = llmin( map_half_height - text_half_height, map_half_width - text_half_width ); text_box->setOrigin( - llround(map_half_width - text_half_width + radius * cos( rotation )), - llround(map_half_height - text_half_height + radius * sin( rotation )) ); + llmath::llround(map_half_width - text_half_width + radius * cos( rotation )), + llmath::llround(map_half_height - text_half_height + radius * sin( rotation )) ); } @@ -1807,8 +1807,8 @@ void LLWorldMapView::reshape( S32 width, S32 height, BOOL called_from_parent ) bool LLWorldMapView::checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track) { LLVector3 pos_view = globalPosToView(item.getGlobalPosition()); - S32 item_x = llround(pos_view.mV[VX]); - S32 item_y = llround(pos_view.mV[VY]); + S32 item_x = llmath::llround(pos_view.mV[VX]); + S32 item_y = llmath::llround(pos_view.mV[VY]); if (x < item_x - BIG_DOT_RADIUS) return false; if (x > item_x + BIG_DOT_RADIUS) return false; @@ -1983,8 +1983,8 @@ BOOL LLWorldMapView::handleMouseDown( S32 x, S32 y, MASK mask ) { gFocusMgr.setMouseCapture( this ); - mMouseDownPanX = llround(sPanX); - mMouseDownPanY = llround(sPanY); + mMouseDownPanX = llmath::llround(sPanX); + mMouseDownPanY = llmath::llround(sPanY); mMouseDownX = x; mMouseDownY = y; sHandledLastClick = TRUE; diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 51d47563e..aa0529bb1 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -211,8 +211,11 @@ class WindowsManifest(ViewerManifest): self.end_prefix() # For mesh upload - if self.prefix(src=self.args['configuration'], dst=""): + if not self.is_win64() and self.prefix(src=self.args['configuration'], dst=""): self.path("libcollada14dom22.dll") + self.end_prefix() + + if self.prefix(src=self.args['configuration'], dst=""): self.path("glod.dll") self.end_prefix() diff --git a/indra/tools/vstool/VSTool.csproj b/indra/tools/vstool/VSTool.csproj index 5d8764b6b..6cd1890fe 100644 --- a/indra/tools/vstool/VSTool.csproj +++ b/indra/tools/vstool/VSTool.csproj @@ -1,4 +1,5 @@ - + + Local 8.0.50727 @@ -25,6 +26,8 @@ + v2.0 + 2.0 .\ diff --git a/indra/tools/vstool/VSTool.exe b/indra/tools/vstool/VSTool.exe index 7ded46ecd84990ca8b80853d4c231a5e3f55ea79..1f0b88beba014eeb81fac23655e50991c4dff6ad 100755 GIT binary patch delta 909 zcmZuwTSyd982-=f?(8_Tt2?f(dlT2o%5Lk_?z$Hjq}+>k!9ZBl9)b|;K?>3%VfEnZ zOBoa;r-B|LDGH*rhk22ZSix5*x)~%AM9`ZAQFOuWpIH@zX5f6^ch3L+=ljp$Ocu#x zk=&Rzx*q+=&s_DA7c-9s$QedRfh?<2q)_Rb?E(Eej3Wmo@F@U2^#BC548XH*B&_z@ z=6PaNOO7St{f-M3T&oc|zm!@)1dGI=bi+9c8+T z$OSxDa?FLpMSia*Y=8mm!iruUB9%lKF$7SE98Dt+NZ6cYrui~wuU(DuN#wFo3*zl2 z*+hq#$=zrf+sUMNSex7LTG>vgAGeuZkOi7&qwb(jFCvwTSY!+RwG(5Nt#psXp1Tin zNs0Y&x7S^lG}oG`2>}mhw=vf13H-;>6;E^j7tGTeu$yHwB^I=8q74Z8WB{*+Z=p#| zS7Gg1F=VT#J#MWK9{TZO4~&*dC0>!}F$^g0zV5aQbdDLk?L$9zCd-y~w!*VYTeo(w z)Lz4rC3ars9o&7Hw+d8iyQ18+Q8v=E7+RytT}IYDb`zaq_q=niHEg@7FApEQ05-h* zrbB&4a)@8mQ+4|dsl_02xD8LaDF{MGsH~Q_X=sI9YgxTPrGP`x)Fo#pX@Pz4NnP<> z7MoM?bgrvA5m7R2k!~fHP9~L1dp4#dGTH82I2X%ga|!i)=&wMc>|88rmHHtI5&Xjd zezp;DvSREr8;|=*RJ|LQjJCmzxm)$>_|b*s(=T7WU3{-Dq=vZan_I6(b1<)Q-%Nvp Z%&J`-7Yu4;=kF@6*rCe38&xx5^FL;tyX61? delta 761 zcmZuuO-vI}5T3Vm+wE>kyKcp9cN>;Mp__K4ztAd)U^gI`|~MbqtCZ33Kgj@}Hgz!?CMP5?x)2;e2ZP}Z)6-*^#g zA9idab%zE5G#25Rq)=I>=s=p{0X#-sB|Fh7dy@l8Az4{vY8>g0YPn@f@aYQPa%>Lo zJ66YWf0OURV~&;a9mk4ziM*CId7GlPR1kn?u%8DjB~Uej4=d!0oD%yeBYK))uf&Qh z&XIU55jag*HwRxK=VM8Ev6YCITb{&;94+!FrfW_S!7UOH19VN9q5MzM3#F%TjpjMhC7kU?FZjqU)3jG&hO48mI}Jfow~*xGbfsT-E+C=YmKfB cRI?X{?zcT(^b diff --git a/indra/tools/vstool/VSTool.sln b/indra/tools/vstool/VSTool.sln index 543a0a2ef..4d83fac34 100644 --- a/indra/tools/vstool/VSTool.sln +++ b/indra/tools/vstool/VSTool.sln @@ -1,5 +1,7 @@ -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30723.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VSTool", "VSTool.csproj", "{96943E2D-1373-4617-A117-D0F997A94919}" EndProject Global diff --git a/indra/tools/vstool/main.cs b/indra/tools/vstool/main.cs index ddf37e85c..be33ae98b 100644 --- a/indra/tools/vstool/main.cs +++ b/indra/tools/vstool/main.cs @@ -556,7 +556,7 @@ namespace VSTool break; case "12.00": - version = "VC110"; + version = "VC120"; break; default: throw new ApplicationException("Unknown .sln version: " + format); @@ -601,6 +601,11 @@ namespace VSTool case "VC110": progid = "VisualStudio.DTE.11.0"; break; + + case "VC120": + progid = "VisualStudio.DTE.12.0"; + break; + default: throw new ApplicationException("Can't handle VS version: " + version); } diff --git a/install.xml b/install.xml index a703de3fc..e3cbe4f88 100644 --- a/install.xml +++ b/install.xml @@ -67,9 +67,9 @@ windows64 md5sum - c6d96cc9f6d993e2147710a5fb0b089a + 54684a0e539879c4e20839929faca547 url - https://bitbucket.org/SingularityViewer/libraries/downloads/glod-1.0pre4-windows64-20131028.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/glod-1.0pre4-windows64-vs12-20140723.tar.bz2 @@ -136,9 +136,9 @@ windows64 md5sum - 7dcae03cad9bc04ac7e937284e6d102e + 1a3dbaf92136ca05e57c92c9c09cbff8 url - https://bitbucket.org/SingularityViewer/libraries/downloads/apr_suite-1.4.5-windows64-20131019.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/apr_suite-1.4.5-windows64-vs12-20140710.tar.bz2 @@ -183,9 +183,9 @@ windows64 md5sum - 2ca54250b59170f3e5342dacad529859 + b65ca4ce2e9cc34f3ac545ba3bfbdb3a url - https://bitbucket.org/SingularityViewer/libraries/downloads/ares-1.9.1-windows64-20131019.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/ares-1.10.0-windows64-vs12-20140709.tar.bz2 @@ -230,9 +230,9 @@ windows64 md5sum - 68c056e920ba2b25c0acded6f482a2e4 + 2b33deae4df210c8bb9e1aea6174bc13 url - https://bitbucket.org/SingularityViewer/libraries/downloads/boost-1.53.0-windows64-20131019.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/boost-1.56.0-windows64-vs12-20140816.tar.bz2 @@ -277,9 +277,9 @@ windows64 md5sum - acbe863e1b3df636e1245cc27c280e7b + 53b5cd120718119b8a31b5073a49360e url - https://bitbucket.org/SingularityViewer/libraries/downloads/colladadom-2.2-windows64-20131020.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/colladadom-2.3-windows64-vs12-20140816.tar.bz2 @@ -324,9 +324,9 @@ windows64 md5sum - ffb31d596f41b650bed49ac13bb513de + 3820517a958655c31890eaf71bb284ba url - https://bitbucket.org/SingularityViewer/libraries/downloads/curl-7.24.0-windows64-20131019.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/curl-7.37.1-windows64-vs12-20140722.tar.bz2 @@ -445,9 +445,9 @@ windows64 md5sum - 3239d5c50d6f2c857cc1a95674dbd1f5 + b68a9abb802abf52f63c805a589eb3fc url - https://bitbucket.org/SingularityViewer/libraries/downloads/expat-2.1.0-windows64-20131019.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/expat-2.1.0-windows64-vs12-20140709.tar.bz2 @@ -514,9 +514,9 @@ windows64 md5sum - bfde86bbd84536448ac2a717ed1646d8 + 4a33389ffa875bfd4554fa4d8198692c url - https://bitbucket.org/SingularityViewer/libraries/downloads/freetype-2.5.0.1-windows64-20131020.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/freetype-2.5.3-windows64-vs12-20140725.tar.bz2 @@ -607,9 +607,9 @@ windows64 md5sum - eeec9982df843043a18748276bbf39ce + 919c3fdfac84aaa69c84f1ce793cd9ff url - https://bitbucket.org/SingularityViewer/libraries/downloads/glui-2.36-windows-20110214.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/glui-2.36-windows64-vs12-20140710.tar.bz2 @@ -650,9 +650,9 @@ windows64 md5sum - f9072dda75012868d023c335ea5bcf88 + 61f46d94a137f8a50bc6328ed8a69762 url - https://bitbucket.org/SingularityViewer/libraries/downloads/google_breakpad-0.0.0-rev1099-windows64-20140606.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/google_breakpad-1351-windows64-vs12-20140723.tar.bz2 @@ -771,9 +771,9 @@ windows64 md5sum - 22fd1388454117626c2477509d3e93be + b97500817a949b250820ca300014d2c6 url - https://bitbucket.org/SingularityViewer/libraries/downloads/libhunspell-1.3.2-windows64-20140605.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/libhunspell-1.3.2-windows64-vs12-20140709.tar.bz2 @@ -818,9 +818,9 @@ windows64 md5sum - 79e328b10fae2090262c0bf02c9c5f71 + d687c074b621715499bfe6e9ba1acbd2 url - https://bitbucket.org/SingularityViewer/libraries/downloads/jpeglib-8c-windows64-20131020.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/libjpeg_turbo-1.3.1-windows64-vs12-20140709.tar.bz2 @@ -865,9 +865,9 @@ windows64 md5sum - 8cf95eef2a95b71eb4a8ab59779bed52 + a6de6a8188a87ce8048847f37b4ecd8f url - https://bitbucket.org/SingularityViewer/libraries/downloads/jsoncpp-0.5.0-windows64-20131020.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/jsoncpp-0.5.0-windows64-vs12-20140709.tar.bz2 @@ -912,9 +912,9 @@ windows64 md5sum - 00fc7bcb4016ecc57def9e3b3223d977 + 82ac61f5d208b0c907217fc90bb835dd url - https://bitbucket.org/SingularityViewer/libraries/downloads/libpng-1.5.10-windows64-20131020.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/libpng-1.6.8-windows64-vs12-20140709.tar.bz2 @@ -964,6 +964,13 @@ url https://bitbucket.org/SingularityViewer/libraries/downloads/libxml2-2.7.8-linux-x86_64-20120420.tar.bz2 + windows64 + + md5sum + 1c2effa8c26de72f6c92f65f83dd9e29 + url + https://bitbucket.org/SingularityViewer/libraries/downloads/libxml2-2.9.1-windows64-vs12-20140709.tar.bz2 + llqtwebkit @@ -1003,9 +1010,9 @@ windows64 md5sum - cf9e5d1d79531a7e4d45f78657da9c9b + a1c9564dbce8f3b0a296f9924e68a221 url - https://bitbucket.org/SingularityViewer/libraries/downloads/llqtwebkit-4.7.1-windows64-20131021.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/llqtwebkit-4.8.6-windows64-vs12-20140710.tar.bz2 @@ -1102,9 +1109,9 @@ windows64 md5sum - b59b4ddab26d4441829f50b48925baf0 + c0fd9371a94cd230c6076c80c7d83a1a url - https://bitbucket.org/SingularityViewer/libraries/downloads/libndofdev-0.1-windows64-20131020.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/libndofdev-0.1-windows64-vs12-20140710.tar.bz2 @@ -1149,9 +1156,9 @@ windows64 md5sum - a8ca0eef3d74936d504dab52ecc61ced + d074bd556211fd29c58326b2898b0a12 url - https://bitbucket.org/SingularityViewer/libraries/downloads/ogg_vorbis-1.2.2-1.3.2-windows64-20131020.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/ogg_vorbis-1.3.2-1.3.4-windows64-vs12-20140709.tar.bz2 @@ -1192,9 +1199,9 @@ windows64 md5sum - b50166f0b0a275c8ea0d3b11c578d792 + 66eba34f543bdd90e0656a9c97c8afae url - https://bitbucket.org/SingularityViewer/libraries/downloads/openssl-1.0.0g-windows64-20131019.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/openssl-1.0.1h-windows64-vs12-20140709.tar.bz2 @@ -1232,9 +1239,9 @@ windows64 md5sum - e75f1529adcaa6e508d1725f59d93a16 + 62f9844903ae76d2ae90d17ca5814060 url - https://bitbucket.org/SingularityViewer/libraries/downloads/openal-1.12.854-1.1.0-windows-20110301.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/openal-1.15.1-1.1.0-windows64-vs12-20140813.tar.bz2 @@ -1269,6 +1276,13 @@ url https://bitbucket.org/SingularityViewer/libraries/downloads/pcre-7.6-linux64-20130216.tar.bz2 + windows64 + + md5sum + ffdbc30ce4b9238070917073b6066b36 + url + https://bitbucket.org/SingularityViewer/libraries/downloads/pcre-8.35-windows64-vs12-20140816.tar.bz2 + pulseaudio @@ -1402,9 +1416,9 @@ windows64 md5sum - 94c26b93b855f5816bb29f308d9997fb + 56175d424e533bbc111a32054fbd2926 url - https://bitbucket.org/SingularityViewer/libraries/downloads/xmlrpc_epi-0.54.1-windows64-20131019.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/xmlrpc_epi-0.54.1-windows64-vs12-20140709.tar.bz2 @@ -1449,9 +1463,9 @@ windows64 md5sum - 5aa50bd41d6cf0262a94760ef66bdbcf + 3f1ca95ca4461aac57e7255c42db3ebc url - https://bitbucket.org/SingularityViewer/libraries/downloads/zlib-1.2.8-windows64-20131019.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/zlib-1.2.8-windows64-vs12-20140709.tar.bz2 From e49156a074f951cbca755f2a445b5e1510e0118c Mon Sep 17 00:00:00 2001 From: Drake Arconis Date: Fri, 22 Aug 2014 00:29:46 -0400 Subject: [PATCH 04/55] Fix mesh upload broken by # --- indra/newview/llfloatermodelpreview.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 26f4c136c..da11cbaf9 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -1446,8 +1446,11 @@ bool LLModelLoader::doLoadModel() } //no suitable slm exists, load from the .dae file + //replace illegal # in path as collada's escapser is broken + std::string tmp_file = mFilename; + boost::replace_all(tmp_file, "#", "%23"); DAE dae; - domCOLLADA* dom = dae.open(mFilename); + domCOLLADA* dom = dae.open(tmp_file); if (!dom) { @@ -1474,7 +1477,7 @@ bool LLModelLoader::doLoadModel() daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH); - daeDocument* doc = dae.getDoc(mFilename); + daeDocument* doc = dae.getDoc(tmp_file); if (!doc) { llwarns << "can't find internal doc" << llendl; From 37c7a725058cad6131987ba4cc9f423becfe3bc6 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 7 Sep 2014 17:18:55 -0500 Subject: [PATCH 05/55] Implement boost and stl mutexes and atomics. --- indra/aistatemachine/aistatemachine.cpp | 6 +- indra/llcommon/CMakeLists.txt | 2 + indra/llcommon/aiframetimer.cpp | 22 +- indra/llcommon/aiframetimer.h | 2 +- indra/llcommon/aithreadsafe.h | 8 +- indra/llcommon/llapr.cpp | 54 ---- indra/llcommon/llapr.h | 43 --- indra/llcommon/llaprpool.cpp | 35 +-- indra/llcommon/llaprpool.h | 11 +- indra/llcommon/llatomic.h | 44 ++- indra/llcommon/llerror.cpp | 21 +- indra/llcommon/llframetimer.cpp | 10 +- indra/llcommon/llframetimer.h | 27 +- indra/llcommon/llsingleton.h | 4 +- indra/llcommon/llthread.cpp | 241 ++++++++------- indra/llcommon/llthread.h | 386 +++++++++++++++++------- indra/llmath/llcalcparser.h | 59 ++-- indra/llmessage/aicurl.cpp | 2 +- indra/llmessage/aicurlprivate.h | 2 +- indra/llmessage/aicurlthread.cpp | 6 +- indra/llmessage/llpumpio.cpp | 68 ++--- indra/llmessage/llpumpio.h | 13 +- 22 files changed, 583 insertions(+), 483 deletions(-) diff --git a/indra/aistatemachine/aistatemachine.cpp b/indra/aistatemachine/aistatemachine.cpp index fe7b4a3bd..134461e09 100644 --- a/indra/aistatemachine/aistatemachine.cpp +++ b/indra/aistatemachine/aistatemachine.cpp @@ -440,7 +440,7 @@ void AIStateMachine::multiplex(event_type event) // our need to run (by us having set need_run), so there is no need to run // ourselves. llassert(!mMultiplexMutex.isSelfLocked()); // We may never enter recursively! - if (!mMultiplexMutex.tryLock()) + if (!mMultiplexMutex.try_lock()) { Dout(dc::statemachine(mSMDebug), "Leaving because it is already being run [" << (void*)this << "]"); return; @@ -762,7 +762,7 @@ void AIStateMachine::multiplex(event_type event) //========================================= // Release the lock on mMultiplexMutex *first*, before releasing the lock on mState, - // to avoid to ever call the tryLock() and fail, while this thread isn't still + // to avoid to ever call the try_lock() and fail, while this thread isn't still // BEFORE the critical area of mState! mMultiplexMutex.unlock(); @@ -1262,7 +1262,7 @@ void AIStateMachine::abort(void) multiplex(insert_abort); } // Block until the current run finished. - if (!mRunMutex.tryLock()) + if (!mRunMutex.try_lock()) { llwarns << "AIStateMachine::abort() blocks because the statemachine is still executing code in another thread." << llendl; mRunMutex.lock(); diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 79674c5a0..ccc48860d 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -284,6 +284,8 @@ target_link_libraries( ${WINDOWS_LIBRARIES} ${Boost_CONTEXT_LIBRARY} ${Boost_REGEX_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${Boost_SYSTEM_LIBRARY} ${CORESERVICES_LIBRARY} ) diff --git a/indra/llcommon/aiframetimer.cpp b/indra/llcommon/aiframetimer.cpp index 3c102a9c7..4e5c104c1 100644 --- a/indra/llcommon/aiframetimer.cpp +++ b/indra/llcommon/aiframetimer.cpp @@ -65,7 +65,7 @@ static F64 const NEVER = 1e16; // 317 million years. F64 AIFrameTimer::sNextExpiration; AIFrameTimer::timer_list_type AIFrameTimer::sTimerList; -LLMutex AIFrameTimer::sMutex; +LLGlobalMutex AIFrameTimer::sMutex; // Notes on thread-safety of AIRunningFrameTimer (continued from aiframetimer.h) // @@ -80,11 +80,10 @@ LLMutex AIFrameTimer::sMutex; void AIFrameTimer::create(F64 expiration, signal_type::slot_type const& slot) { AIRunningFrameTimer new_timer(expiration, this); - sMutex.lock(); + LLMutexLock lock(sMutex); llassert(mHandle.mRunningTimer == sTimerList.end()); // Create may only be called when the timer isn't already running. mHandle.init(sTimerList.insert(new_timer), slot); sNextExpiration = sTimerList.begin()->expiration(); - sMutex.unlock(); } void AIFrameTimer::cancel(void) @@ -95,18 +94,19 @@ void AIFrameTimer::cancel(void) // mHandle.mMutex lock), we start with trying to obtain // it here and as such wait till the callback function // returned. - mHandle.mMutex.lock(); + mHandle.mMutex.lock(); // Next we have to grab this lock in order to stop // AIFrameTimer::handleExpiration from even entering // in the case we manage to get it first. - sMutex.lock(); - if (mHandle.mRunningTimer != sTimerList.end()) { - sTimerList.erase(mHandle.mRunningTimer); - mHandle.mRunningTimer = sTimerList.end(); - sNextExpiration = sTimerList.empty() ? NEVER : sTimerList.begin()->expiration(); + LLMutexLock lock(sMutex); + if (mHandle.mRunningTimer != sTimerList.end()) + { + sTimerList.erase(mHandle.mRunningTimer); + mHandle.mRunningTimer = sTimerList.end(); + sNextExpiration = sTimerList.empty() ? NEVER : sTimerList.begin()->expiration(); + } } - sMutex.unlock(); mHandle.mMutex.unlock(); } @@ -164,7 +164,7 @@ void AIFrameTimer::handleExpiration(F64 current_frame_time) // // Note that if the other thread actually obtained the sMutex then we // can't be here: this is still inside the critical area of sMutex. - if (handle.mMutex.tryLock()) // If this fails then another thread is in the process of cancelling this timer, so do nothing. + if (handle.mMutex.try_lock()) // If this fails then another thread is in the process of cancelling this timer, so do nothing. { sMutex.unlock(); running_timer->do_callback(); // May not throw exceptions. diff --git a/indra/llcommon/aiframetimer.h b/indra/llcommon/aiframetimer.h index 366581c49..880b492ee 100644 --- a/indra/llcommon/aiframetimer.h +++ b/indra/llcommon/aiframetimer.h @@ -96,7 +96,7 @@ class LL_COMMON_API AIFrameTimer typedef std::multiset timer_list_type; - static LLMutex sMutex; // Mutex for the two global variables below. + static LLGlobalMutex sMutex; // Mutex for the two global variables below. static timer_list_type sTimerList; // List with all running timers. static F64 sNextExpiration; // Cache of smallest value in sTimerList. friend class LLFrameTimer; // Access to sNextExpiration. diff --git a/indra/llcommon/aithreadsafe.h b/indra/llcommon/aithreadsafe.h index bb0ac7e05..93b38246f 100644 --- a/indra/llcommon/aithreadsafe.h +++ b/indra/llcommon/aithreadsafe.h @@ -239,7 +239,7 @@ protected: // For use by AIThreadSafeDC AIThreadSafe(void) { } - AIThreadSafe(LLAPRPool& parent) : mRWLock(parent) { } + MUTEX_POOL(AIThreadSafe(LLAPRPool& parent) : mRWLock(parent){ }) public: // Only for use by AITHREADSAFE, see below. @@ -473,7 +473,7 @@ protected: friend struct AIRegisteredStateMachinesList; // For use by AIThreadSafeSimpleDC and AIRegisteredStateMachinesList. AIThreadSafeSimple(void) { } - AIThreadSafeSimple(LLAPRPool& parent) : mMutex(parent) { } + MUTEX_POOL(AIThreadSafeSimple(LLAPRPool& parent) : mMutex(parent) { }) public: // Only for use by AITHREADSAFESIMPLE, see below. @@ -551,7 +551,7 @@ public: protected: // For use by AIThreadSafeSimpleDCRootPool - AIThreadSafeSimpleDC(LLAPRRootPool& parent) : AIThreadSafeSimple(parent) { new (AIThreadSafeSimple::ptr()) T; } + AIThreadSafeSimpleDC(LLAPRRootPool& parent) : AIThreadSafeSimple(MUTEX_POOL(parent)) { new (AIThreadSafeSimple::ptr()) T; } }; // Helper class for AIThreadSafeSimpleDCRootPool to assure initialization of @@ -585,7 +585,7 @@ public: // as opposed to allocated from the current threads root pool. AIThreadSafeSimpleDCRootPool(void) : AIThreadSafeSimpleDCRootPool_pbase(), - AIThreadSafeSimpleDC(mRootPool) { } + AIThreadSafeSimpleDC(MUTEX_POOL(mRootPool)) { } }; /** diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp index 4b86fecdf..28e8db756 100644 --- a/indra/llcommon/llapr.cpp +++ b/indra/llcommon/llapr.cpp @@ -30,60 +30,6 @@ #include "llapr.h" #include "llscopedvolatileaprpool.h" -LLFastTimer::DeclareTimer FT_WAIT_FOR_SCOPEDLOCK("LLScopedLock"); - -//--------------------------------------------------------------------- -// -// LLScopedLock -// -LLScopedLock::LLScopedLock(apr_thread_mutex_t* mutex) : mMutex(mutex) -{ - mLocked = !!mutex; - if (LL_LIKELY(mutex)) - { - apr_status_t status = apr_thread_mutex_trylock(mMutex); - while (LL_UNLIKELY(status != APR_SUCCESS)) - { - if (APR_STATUS_IS_EBUSY(status)) - { - if (AIThreadID::in_main_thread_inline()) - { - LLFastTimer ft1(FT_WAIT_FOR_SCOPEDLOCK); - status = apr_thread_mutex_lock(mMutex); - } - else - { - status = apr_thread_mutex_lock(mMutex); - } - } - else - { - ll_apr_warn_status(status); - mLocked = false; - return; - } - } - } -} - -LLScopedLock::~LLScopedLock() -{ - unlock(); -} - -void LLScopedLock::unlock() -{ - if(mLocked) - { - if(!ll_apr_warn_status(apr_thread_mutex_unlock(mMutex))) - { - mLocked = false; - } - } -} - -//--------------------------------------------------------------------- - bool ll_apr_warn_status(apr_status_t status) { if(APR_SUCCESS == status) return false; diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index 97aa96e0b..9baddef5f 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -36,55 +36,12 @@ #include #include "apr_thread_proc.h" -#include "apr_thread_mutex.h" #include "apr_getopt.h" -#include "apr_signal.h" -#include "apr_atomic.h" #include "llstring.h" class LLAPRPool; class LLVolatileAPRPool; -/** - * @class LLScopedLock - * @brief Small class to help lock and unlock mutexes. - * - * This class is used to have a stack level lock once you already have - * an apr mutex handy. The constructor handles the lock, and the - * destructor handles the unlock. Instances of this class are - * not thread safe. - */ -class LL_COMMON_API LLScopedLock : private boost::noncopyable -{ -public: - /** - * @brief Constructor which accepts a mutex, and locks it. - * - * @param mutex An allocated APR mutex. If you pass in NULL, - * this wrapper will not lock. - */ - LLScopedLock(apr_thread_mutex_t* mutex); - - /** - * @brief Destructor which unlocks the mutex if still locked. - */ - ~LLScopedLock(); - - /** - * @brief Check lock. - */ - bool isLocked() const { return mLocked; } - - /** - * @brief This method unlocks the mutex. - */ - void unlock(); - -protected: - bool mLocked; - apr_thread_mutex_t* mMutex; -}; - // File IO convenience functions. // Returns NULL if the file fails to open, sets *sizep to file size if not NULL // abbreviated flags diff --git a/indra/llcommon/llaprpool.cpp b/indra/llcommon/llaprpool.cpp index 3dffa8300..b065a5801 100644 --- a/indra/llcommon/llaprpool.cpp +++ b/indra/llcommon/llaprpool.cpp @@ -37,6 +37,7 @@ #include "llerror.h" #include "llaprpool.h" +#include "llatomic.h" #include "llthread.h" // Create a subpool from parent. @@ -110,23 +111,18 @@ LLAPRInitialization::LLAPRInitialization(void) } bool LLAPRRootPool::sCountInitialized = false; -apr_uint32_t volatile LLAPRRootPool::sCount; - -apr_thread_mutex_t* gLogMutexp; -apr_thread_mutex_t* gCallStacksLogMutexp; +LLAtomicS32 LLAPRRootPool::sCount; LLAPRRootPool::LLAPRRootPool(void) : LLAPRInitialization(), LLAPRPool(0) { // sCountInitialized don't need locking because when we get here there is still only a single thread. if (!sCountInitialized) { - // Initialize the logging mutex - apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_UNNESTED, mPool); - apr_thread_mutex_create(&gCallStacksLogMutexp, APR_THREAD_MUTEX_UNNESTED, mPool); - +#ifdef NEEDS_APR_ATOMICS apr_status_t status = apr_atomic_init(mPool); llassert_always(status == APR_SUCCESS); - apr_atomic_set32(&sCount, 1); // Set to 1 to account for the global root pool. +#endif + sCount = 1; // Set to 1 to account for the global root pool. sCountInitialized = true; // Initialize thread-local APR pool support. @@ -134,33 +130,16 @@ LLAPRRootPool::LLAPRRootPool(void) : LLAPRInitialization(), LLAPRPool(0) // it must be done last, so that sCount is already initialized. LLThreadLocalData::init(); } - apr_atomic_inc32(&sCount); + sCount++; } LLAPRRootPool::~LLAPRRootPool() { - if (!apr_atomic_dec32(&sCount)) + if (!--sCount) { // The last pool was destructed. Cleanup remainder of APR. LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL; - if (gLogMutexp) - { - // Clean up the logging mutex - - // All other threads NEED to be done before we clean up APR, so this is okay. - apr_thread_mutex_destroy(gLogMutexp); - gLogMutexp = NULL; - } - if (gCallStacksLogMutexp) - { - // Clean up the logging mutex - - // All other threads NEED to be done before we clean up APR, so this is okay. - apr_thread_mutex_destroy(gCallStacksLogMutexp); - gCallStacksLogMutexp = NULL; - } - // Must destroy ALL, and therefore this last LLAPRRootPool, before terminating APR. static_cast(this)->destroy(); diff --git a/indra/llcommon/llaprpool.h b/indra/llcommon/llaprpool.h index 74af351e4..d3606d396 100644 --- a/indra/llcommon/llaprpool.h +++ b/indra/llcommon/llaprpool.h @@ -47,9 +47,11 @@ #include "apr_portable.h" #include "apr_pools.h" +#include "llatomic.h" #include "llerror.h" #include "aithreadid.h" + extern void ll_init_apr(); /** @@ -104,8 +106,11 @@ public: // NEVER destroy a pool that is returned by this function! apr_pool_t* operator()(void) const { - llassert(mPool); - llassert(mOwner.equals_current_thread()); + if (mParent) + { + llassert(mPool); + llassert(mOwner.equals_current_thread()); + } return mPool; } @@ -181,7 +186,7 @@ private: private: // Keep track of how many root pools exist and when the last one is destructed. static bool sCountInitialized; - static apr_uint32_t volatile sCount; + static LLAtomicS32 sCount; public: // Return a global root pool that is independent of LLThreadLocalData. diff --git a/indra/llcommon/llatomic.h b/indra/llcommon/llatomic.h index 3cc5bcf38..0cfe99c77 100644 --- a/indra/llcommon/llatomic.h +++ b/indra/llcommon/llatomic.h @@ -34,8 +34,29 @@ #ifndef LL_LLATOMIC_H #define LL_LLATOMIC_H -#include "apr_atomic.h" +#define USE_BOOST_ATOMIC +//Internal definitions +#define NEEDS_APR_ATOMICS do_not_define_manually_thanks +#undef NEEDS_APR_ATOMICS + +#if defined(USE_BOOST_ATOMIC) +#include "boost/version.hpp" +#endif + +//Prefer boost over stl over apr. + +#if defined(USE_BOOST_ATOMIC) && (BOOST_VERSION >= 105200) +#include "boost/atomic.hpp" +template +struct impl_atomic_type { typedef boost::atomic type; }; +#elif defined(USE_STD_ATOMIC) && (__cplusplus >= 201103L || _MSC_VER >= 1800) +#include +template +struct impl_atomic_type { typedef std::atomic type; }; +#else +#include "apr_atomic.h" +#define NEEDS_APR_ATOMICS template class LLAtomic32 { public: @@ -54,6 +75,27 @@ public: private: apr_uint32_t mData; }; +#endif +#if !defined(NEEDS_APR_ATOMICS) +template class LLAtomic32 +{ +public: + LLAtomic32(void) { } + LLAtomic32(LLAtomic32 const& atom) { mData = Type(atom.mData); } + LLAtomic32(Type x) { mData = x; } + LLAtomic32& operator=(LLAtomic32 const& atom) { mData = Type(atom.mData); return *this; } + + operator Type() const { return mData; } + void operator=(Type x) { mData = x; } + void operator-=(Type x) { mData -= x; } + void operator+=(Type x) { mData += x; } + Type operator++(int) { return mData++; } // Type++ + bool operator--() { return --mData; } // Returns (--Type != 0) + +private: + typename impl_atomic_type::type mData; +}; +#endif typedef LLAtomic32 LLAtomicU32; typedef LLAtomic32 LLAtomicS32; diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 8b40edb40..4155885d8 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -874,8 +874,8 @@ You get: */ -extern apr_thread_mutex_t* gLogMutexp; -extern apr_thread_mutex_t* gCallStacksLogMutexp; +LLGlobalMutex gLogMutex; +LLGlobalMutex gCallStacksLogMutex; namespace { bool checkLevelMap(const LevelMap& map, const std::string& key, @@ -905,17 +905,15 @@ namespace { LogLock::LogLock() : mLocked(false), mOK(false) { - if (!gLogMutexp) + if (!gLogMutex.isInitalized()) { mOK = true; return; } - const int MAX_RETRIES = 5; for (int attempts = 0; attempts < MAX_RETRIES; ++attempts) { - apr_status_t s = apr_thread_mutex_trylock(gLogMutexp); - if (!APR_STATUS_IS_EBUSY(s)) + if (gLogMutex.try_lock()) { mLocked = true; mOK = true; @@ -937,7 +935,7 @@ namespace { { if (mLocked) { - apr_thread_mutex_unlock(gLogMutexp); + gLogMutex.unlock(); } } } @@ -1299,17 +1297,16 @@ namespace LLError CallStacksLogLock::CallStacksLogLock() : mLocked(false), mOK(false) { - if (!gCallStacksLogMutexp) + if (!gCallStacksLogMutex.isInitalized()) { mOK = true; return; } - + const int MAX_RETRIES = 5; for (int attempts = 0; attempts < MAX_RETRIES; ++attempts) { - apr_status_t s = apr_thread_mutex_trylock(gCallStacksLogMutexp); - if (!APR_STATUS_IS_EBUSY(s)) + if (gCallStacksLogMutex.try_lock()) { mLocked = true; mOK = true; @@ -1328,7 +1325,7 @@ namespace LLError { if (mLocked) { - apr_thread_mutex_unlock(gCallStacksLogMutexp); + gCallStacksLogMutex.unlock(); } } diff --git a/indra/llcommon/llframetimer.cpp b/indra/llcommon/llframetimer.cpp index c214f03a4..94f0c1dee 100644 --- a/indra/llcommon/llframetimer.cpp +++ b/indra/llcommon/llframetimer.cpp @@ -47,12 +47,14 @@ S32 LLFrameTimer::sFrameCount = 0; // Current frame number (number of fr U64 LLFrameTimer::sPrevTotalTime = LLFrameTimer::sStartTotalTime; // Previous (frame) time in microseconds since epoch, updated once per frame. U64 LLFrameTimer::sFrameDeltaTime = 0; // Microseconds between last two calls to LLFrameTimer::updateFrameTimeAndCount. // Mutex for the above. -apr_thread_mutex_t* LLFrameTimer::sGlobalMutex; +LLGlobalMutex LLFrameTimer::sGlobalMutex; + +bool LLFrameTimer::sFirstFrameTimerCreated; // static void LLFrameTimer::global_initialization(void) { - apr_thread_mutex_create(&sGlobalMutex, APR_THREAD_MUTEX_UNNESTED, LLAPRRootPool::get()()); + sFirstFrameTimerCreated = true; AIFrameTimer::sNextExpiration = NEVER; } @@ -63,9 +65,9 @@ void LLFrameTimer::updateFrameTime(void) sTotalTime = totalTime(); sTotalSeconds = U64_to_F64(sTotalTime) * USEC_TO_SEC_F64; F64 new_frame_time = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64; - apr_thread_mutex_lock(sGlobalMutex); + sGlobalMutex.lock(); sFrameTime = new_frame_time; - apr_thread_mutex_unlock(sGlobalMutex); + sGlobalMutex.unlock(); } // static diff --git a/indra/llcommon/llframetimer.h b/indra/llcommon/llframetimer.h index 30bc58dd9..5ebf14247 100644 --- a/indra/llcommon/llframetimer.h +++ b/indra/llcommon/llframetimer.h @@ -36,16 +36,13 @@ #include "lltimer.h" #include "timing.h" -#include -#ifdef SHOW_ASSERT -#include "aithreadid.h" // is_main_thread() -#endif +#include "llthread.h" class LL_COMMON_API LLFrameTimer { public: // Create an LLFrameTimer and start it. After creation it is running and in the state expired (hasExpired will return true). - LLFrameTimer(void) : mExpiry(0), mRunning(true), mPaused(false) { if (!sGlobalMutex) global_initialization(); setAge(0.0); } + LLFrameTimer(void) : mExpiry(0), mRunning(true), mPaused(false) { if (!sFirstFrameTimerCreated) global_initialization(); setAge(0.0); } // void copy(LLFrameTimer const& timer) { mStartTime = timer.mStartTime; mExpiry = timer.mExpiry; mRunning = timer.mRunning; mPaused = timer.mPaused; } @@ -57,9 +54,9 @@ public: static F64 getElapsedSeconds(void) { // Loses msec precision after ~4.5 hours... - apr_thread_mutex_lock(sGlobalMutex); + sGlobalMutex.lock(); F64 res = sFrameTime; - apr_thread_mutex_unlock(sGlobalMutex); + sGlobalMutex.unlock(); return res; } @@ -68,9 +65,9 @@ public: { // sTotalTime is only accessed by the main thread, so no locking is necessary. llassert(is_main_thread()); - //apr_thread_mutex_lock(sGlobalMutex); + //sGlobalMutex.lock(); U64 res = sTotalTime; - //apr_thread_mutex_unlock(sGlobalMutex); + //sGlobalMutex.unlock(); llassert(res); return res; } @@ -80,9 +77,9 @@ public: { // sTotalSeconds is only accessed by the main thread, so no locking is necessary. llassert(is_main_thread()); - //apr_thread_mutex_lock(sGlobalMutex); + //sGlobalMutex.lock(); F64 res = sTotalSeconds; - //apr_thread_mutex_unlock(sGlobalMutex); + //sGlobalMutex.unlock(); return res; } @@ -91,9 +88,9 @@ public: { // sFrameCount is only accessed by the main thread, so no locking is necessary. llassert(is_main_thread()); - //apr_thread_mutex_lock(sGlobalMutex); + //sGlobalMutex.lock(); U32 res = sFrameCount; - //apr_thread_mutex_unlock(sGlobalMutex); + //sGlobalMutex.unlock(); return res; } @@ -170,7 +167,7 @@ protected: // // More than one thread are accessing (some of) these variables, therefore we need locking. - static apr_thread_mutex_t* sGlobalMutex; + static LLGlobalMutex sGlobalMutex; // Current time in seconds since application start, updated together with sTotalTime. static F64 sFrameTime; @@ -190,6 +187,8 @@ protected: // Current frame number (number of frames since application start). static S32 sFrameCount; + static bool sFirstFrameTimerCreated; + // // Member data // diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index a780a6a24..aa8efb1e0 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -33,10 +33,10 @@ /// @brief A global registry of all singletons to prevent duplicate allocations /// across shared library boundaries -class LL_COMMON_API LLSingletonRegistry { +class LLSingletonRegistry { private: typedef std::map TypeMap; - static TypeMap * sSingletonMap; + static LL_COMMON_API TypeMap * sSingletonMap; static void checkInit() { diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 1c3fec608..7ab4b1f39 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -364,138 +364,133 @@ LLThreadLocalData& LLThreadLocalData::tldata(void) //============================================================================ -LLCondition::LLCondition(LLAPRPool& parent) : LLMutex(parent) +#if defined(NEEDS_MUTEX_IMPL) +#if defined(USE_WIN32_THREAD) +LLMutexImpl::LLMutexImpl() { - apr_thread_cond_create(&mAPRCondp, mPool()); + InitializeCriticalSection(&mMutexImpl); //can throw STATUS_NO_MEMORY +} +LLMutexImpl::~LLMutexImpl() +{ + DeleteCriticalSection(&mMutexImpl); //nothrow +} +void LLMutexImpl::lock() +{ + EnterCriticalSection(&mMutexImpl); //can throw EXCEPTION_POSSIBLE_DEADLOCK +} +void LLMutexImpl::unlock() +{ + LeaveCriticalSection(&mMutexImpl); //nothrow +} +bool LLMutexImpl::try_lock() +{ + return !!TryEnterCriticalSection(&mMutexImpl); //nothrow +} +LLConditionVariableImpl::LLConditionVariableImpl() +{ + InitializeConditionVariable(&mConditionVariableImpl); +} +LLConditionVariableImpl::~LLConditionVariableImpl() +{ + //There is no DeleteConditionVariable +} +void LLConditionVariableImpl::notify_one() +{ + WakeConditionVariable(&mConditionVariableImpl); +} +void LLConditionVariableImpl::notify_all() +{ + WakeAllConditionVariable(&mConditionVariableImpl); +} +void LLConditionVariableImpl::wait(LLMutex& lock) +{ + LLMutex::ImplAdoptMutex impl_adopted_mutex(lock); + SleepConditionVariableCS(&mConditionVariableImpl, &lock.native_handle(), INFINITE); +} +#else + +void APRExceptionThrower(apr_status_t status) +{ + if(status != APR_SUCCESS) + { + static char buf[256]; + throw std::logic_error(apr_strerror(status,buf,sizeof(buf))); + } } - -LLCondition::~LLCondition() +LLMutexImpl::LLMutexImpl(native_pool_type& pool) : mPool(pool), mMutexImpl(NULL) { - apr_thread_cond_destroy(mAPRCondp); - mAPRCondp = NULL; + APRExceptionThrower(apr_thread_mutex_create(&mMutexImpl, APR_THREAD_MUTEX_UNNESTED, mPool())); +} +LLMutexImpl::~LLMutexImpl() +{ + APRExceptionThrower(apr_thread_mutex_destroy(mMutexImpl)); + mMutexImpl = NULL; +} +void LLMutexImpl::lock() +{ + APRExceptionThrower(apr_thread_mutex_lock(mMutexImpl)); +} +void LLMutexImpl::unlock() +{ + APRExceptionThrower(apr_thread_mutex_unlock(mMutexImpl)); +} +bool LLMutexImpl::try_lock() +{ + apr_status_t status = apr_thread_mutex_trylock(mMutexImpl); + if(APR_STATUS_IS_EBUSY(status)) + return false; + APRExceptionThrower(status); + return true; +} +LLConditionVariableImpl::LLConditionVariableImpl(native_pool_type& pool) : mPool(pool), mConditionVariableImpl(NULL) +{ + APRExceptionThrower(apr_thread_cond_create(&mConditionVariableImpl, mPool())); +} +LLConditionVariableImpl::~LLConditionVariableImpl() +{ + APRExceptionThrower(apr_thread_cond_destroy(mConditionVariableImpl)); +} +void LLConditionVariableImpl::notify_one() +{ + APRExceptionThrower(apr_thread_cond_signal(mConditionVariableImpl)); +} +void LLConditionVariableImpl::notify_all() +{ + APRExceptionThrower(apr_thread_cond_broadcast(mConditionVariableImpl)); +} +void LLConditionVariableImpl::wait(LLMutex& lock) +{ + LLMutex::ImplAdoptMutex impl_adopted_mutex(lock); + APRExceptionThrower(apr_thread_cond_wait(mConditionVariableImpl, lock.native_handle())); +} +#endif +#endif + +LLFastTimer::DeclareTimer FT_WAIT_FOR_MUTEX("LLMutex::lock()"); +void LLMutex::lock_main(LLFastTimer::DeclareTimer* timer) +{ + llassert(!isSelfLocked()); + LLFastTimer ft1(timer ? *timer : FT_WAIT_FOR_MUTEX); + LLMutexImpl::lock(); } LLFastTimer::DeclareTimer FT_WAIT_FOR_CONDITION("LLCondition::wait()"); - -void LLCondition::wait() +void LLCondition::wait_main() { - if (AIThreadID::in_main_thread_inline()) + llassert(isSelfLocked()); + LLFastTimer ft1(FT_WAIT_FOR_CONDITION); + LLConditionVariableImpl::wait(*this); + llassert(isSelfLocked()); +} + +LLFastTimer::DeclareTimer FT_WAIT_FOR_MUTEXLOCK("LLMutexLock::lock()"); +void LLMutexLock::lock() +{ + if (mMutex) { - LLFastTimer ft1(FT_WAIT_FOR_CONDITION); - apr_thread_cond_wait(mAPRCondp, mAPRMutexp); - } - else - { - apr_thread_cond_wait(mAPRCondp, mAPRMutexp); + mMutex->lock(&FT_WAIT_FOR_MUTEXLOCK); } } -void LLCondition::signal() -{ - apr_thread_cond_signal(mAPRCondp); -} - -void LLCondition::broadcast() -{ - apr_thread_cond_broadcast(mAPRCondp); -} - -//============================================================================ -LLMutexBase::LLMutexBase() : - mCount(0), - mLockingThread(AIThreadID::sNone) -{ -} - -bool LLMutexBase::isSelfLocked() const -{ - return mLockingThread.equals_current_thread_inline(); -} - -LLFastTimer::DeclareTimer FT_WAIT_FOR_MUTEX("LLMutexBase::lock()"); - -void LLMutexBase::lock() -{ - if (mLockingThread.equals_current_thread_inline()) - { //redundant lock - mCount++; - return; - } - - if (APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp))) - { - if (AIThreadID::in_main_thread_inline()) - { - LLFastTimer ft1(FT_WAIT_FOR_MUTEX); - apr_thread_mutex_lock(mAPRMutexp); - } - else - { - apr_thread_mutex_lock(mAPRMutexp); - } - } - - mLockingThread.reset_inline(); -} - -bool LLMutexBase::tryLock() -{ - if (mLockingThread.equals_current_thread_inline()) - { //redundant lock - mCount++; - return true; - } - bool success = !APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)); - if (success) - { - mLockingThread.reset_inline(); - } - return success; -} - -// non-blocking, but does do a lock/unlock so not free -bool LLMutexBase::isLocked() const -{ - if (mLockingThread.equals_current_thread_inline()) - return false; // A call to lock() won't block. - if (APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp))) - return true; - apr_thread_mutex_unlock(mAPRMutexp); - return false; -} - -void LLMutexBase::unlock() -{ - if (mCount > 0) - { //not the root unlock - mCount--; - return; - } - mLockingThread = AIThreadID::sNone; - - apr_thread_mutex_unlock(mAPRMutexp); -} - -//---------------------------------------------------------------------------- - -LLThreadSafeRefCount::LLThreadSafeRefCount() : - mRef(0) -{ -} - -LLThreadSafeRefCount::~LLThreadSafeRefCount() -{ - if (mRef != 0) - { - llerrs << "deleting non-zero reference" << llendl; - } -} - -//============================================================================ - -LLResponder::~LLResponder() -{ -} - -//============================================================================ +//---------------------------------------------------------------------------- \ No newline at end of file diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 7d0cc593c..a3f95e6d8 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -27,6 +27,8 @@ #ifndef LL_LLTHREAD_H #define LL_LLTHREAD_H +#define USE_BOOST_MUTEX + #if LL_GNUC // Needed for is_main_thread() when compiling with optimization (relwithdebinfo). // It doesn't hurt to just always specify it though. @@ -35,10 +37,9 @@ #include "llapp.h" #include "llapr.h" -#include "llmemory.h" -#include "apr_thread_cond.h" #include "llaprpool.h" #include "llatomic.h" +#include "llmemory.h" #include "aithreadid.h" class LLThread; @@ -177,126 +178,294 @@ protected: //============================================================================ -#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO) +#define MUTEX_POOL(arg) -#ifdef MUTEX_DEBUG -// We really shouldn't be using recursive locks. Make sure of that in debug mode. -#define MUTEX_FLAG APR_THREAD_MUTEX_UNNESTED +//Internal definitions +#define NEEDS_MUTEX_IMPL do_not_define_manually_thanks +#undef NEEDS_MUTEX_IMPL +#define NEEDS_MUTEX_RECURSION do_not_define_manually_thanks +#undef NEEDS_MUTEX_RECURSION + +//Prefer boost over stl over windows over apr. + +#if defined(USE_BOOST_MUTEX) && (BOOST_VERSION >= 103400) //condition_variable_any was added in boost 1.34 +//Define BOOST_SYSTEM_NO_DEPRECATED to avoid system_category() and generic_category() dependencies, as those won't be exported. +#define BOOST_SYSTEM_NO_DEPRECATED +#include +#include +#include +#include +typedef boost::recursive_mutex LLMutexImpl; +typedef boost::condition_variable_any LLConditionVariableImpl; +#elif defined(USE_STD_MUTEX) && (__cplusplus >= 201103L || _MSC_VER >= 1800) +#include +typedef std::recursive_mutex LLMutexImpl; +typedef std::condition_variable_any LLConditionVariableImpl; +#elif defined(USE_WIN32_MUTEX) +typedef CRITICAL_SECTION impl_mutex_handle_type; +typedef CONDITION_VARIABLE impl_cond_handle_type; +#define NEEDS_MUTEX_IMPL +#define NEEDS_MUTEX_RECURSION #else -// Use the fastest platform-optimal lock behavior (can be recursive or non-recursive). -#define MUTEX_FLAG APR_THREAD_MUTEX_DEFAULT +//----APR specific------ +#include "apr_thread_cond.h" +#include "apr_thread_mutex.h" +#include "apr_signal.h" +typedef LLAPRPool native_pool_type; +typedef apr_thread_mutex_t* impl_mutex_handle_type; +typedef apr_thread_cond_t* impl_cond_handle_type; +#undef MUTEX_POOL +#undef DEFAULT_POOL +#define MUTEX_POOL(arg) arg +#define NEEDS_MUTEX_IMPL +#define NEEDS_MUTEX_RECURSION +//END #endif -class LL_COMMON_API LLMutexBase +#ifdef NEEDS_MUTEX_IMPL + +//Impl classes are not meant to be accessed directly. They must be utilized by a parent classes. +// They are designed to be 'clones' of their stl counterparts to facilitate simple drop-in +// replacement of underlying implementation (boost,std,apr,critical_sections,etc) +// Members and member functions are all private. +class LL_COMMON_API LLMutexImpl : private boost::noncopyable { -public: - LLMutexBase() ; - - void lock(); // blocks + friend class LLMutex; + friend class LLCondition; + friend class LLConditionVariableImpl; + + typedef impl_mutex_handle_type native_handle_type; + + LLMutexImpl(MUTEX_POOL(native_pool_type& pool)); + virtual ~LLMutexImpl(); + void lock(); void unlock(); - // Returns true if lock was obtained successfully. - bool tryLock(); - - // Returns true if a call to lock() would block (returns false if self-locked()). - bool isLocked() const; - - // Returns true if locked by this thread. - bool isSelfLocked() const; - -protected: - // mAPRMutexp is initialized and uninitialized in the derived class. - apr_thread_mutex_t* mAPRMutexp; - mutable U32 mCount; - mutable AIThreadID mLockingThread; + bool try_lock(); + native_handle_type& native_handle() { return mMutexImpl; } private: - // Disallow copy construction and assignment. - LLMutexBase(LLMutexBase const&); - LLMutexBase& operator=(LLMutexBase const&); + native_handle_type mMutexImpl; + MUTEX_POOL(native_pool_type mPool); }; -class LL_COMMON_API LLMutex : public LLMutexBase -{ -public: - LLMutex(LLAPRPool& parent = LLThread::tldata().mRootPool) : mPool(parent) - { - apr_thread_mutex_create(&mAPRMutexp, MUTEX_FLAG, mPool()); - } - ~LLMutex() - { - //this assertion erroneously triggers whenever an LLCondition is destroyed - //llassert(!isLocked()); // better not be locked! - apr_thread_mutex_destroy(mAPRMutexp); - mAPRMutexp = NULL; - } - -protected: - LLAPRPool mPool; -}; - -#if APR_HAS_THREADS -// No need to use a root pool in this case. -typedef LLMutex LLMutexRootPool; -#else // APR_HAS_THREADS -class LL_COMMON_API LLMutexRootPool : public LLMutexBase -{ -public: - LLMutexRootPool(void) - { - apr_thread_mutex_create(&mAPRMutexp, MUTEX_FLAG, mRootPool()); - } - ~LLMutexRootPool() - { -#if APR_POOL_DEBUG - // It is allowed to destruct root pools from a different thread. - mRootPool.grab_ownership(); #endif - llassert(!isLocked()); // better not be locked! - apr_thread_mutex_destroy(mAPRMutexp); - mAPRMutexp = NULL; + +class LL_COMMON_API LLMutex : public LLMutexImpl +{ +#ifdef NEEDS_MUTEX_IMPL + friend class LLConditionVariableImpl; +#endif +public: + LLMutex(MUTEX_POOL(native_pool_type& pool = LLThread::tldata().mRootPool)) : LLMutexImpl(MUTEX_POOL(pool)), +#ifdef NEEDS_MUTEX_RECURSION + mLockDepth(0), +#endif + mLockingThread(AIThreadID::sNone) + {} + ~LLMutex() + {} + + void lock(LLFastTimer::DeclareTimer* timer = NULL) // blocks + { + if (inc_lock_if_recursive()) + return; + if (AIThreadID::in_main_thread_inline() && LLApp::isRunning()) + { + if (!LLMutexImpl::try_lock()) + { + lock_main(timer); + } + } + else + { + LLMutexImpl::lock(); + } + mLockingThread.reset_inline(); } -protected: - LLAPRRootPool mRootPool; + void unlock() + { +#ifdef NEEDS_MUTEX_RECURSION + if (mLockDepth > 0) + { + --mLockDepth; + return; + } +#endif + mLockingThread = AIThreadID::sNone; + LLMutexImpl::unlock(); + } + + // Returns true if lock was obtained successfully. + bool try_lock() + { + if (inc_lock_if_recursive()) + return true; + if (!LLMutexImpl::try_lock()) + return false; + mLockingThread.reset_inline(); + return true; + } + + // Returns true if locked not by this thread + bool isLocked() + { + if (isSelfLocked()) + return false; + if (LLMutexImpl::try_lock()) + { + LLMutexImpl::unlock(); + return false; + } + return true; + } + // Returns true if locked by this thread. + bool isSelfLocked() const + { + return mLockingThread.equals_current_thread_inline(); + } + +#ifdef NEEDS_MUTEX_IMPL + //This is important for libraries that we cannot pass LLMutex into. + //For example, apr wait. apr wait unlocks and re-locks the thread, however + // it has no knowledge of LLMutex::mLockingThread and LLMutex::mLockDepth, + // and thus will leave those member variables set even after the wait internally releases the lock. + // Leaving those two variables set even when mutex has actually been unlocked via apr is BAD. + friend class ImplAdoptMutex; + class ImplAdoptMutex + { + friend class LLConditionVariableImpl; + ImplAdoptMutex(LLMutex& mutex) : mMutex(mutex), +#ifdef NEEDS_MUTEX_RECURSION + mLockDepth(mutex.mLockDepth), +#endif + mLockingThread(mutex.mLockingThread) + + { + mMutex.mLockingThread = AIThreadID::sNone; +#ifdef NEEDS_MUTEX_RECURSION + mMutex.mLockDepth = 0; +#endif + } + ~ImplAdoptMutex() + { + mMutex.mLockingThread = mLockingThread; +#ifdef NEEDS_MUTEX_RECURSION + mMutex.mLockDepth = mLockDepth; +#endif + } + LLMutex& mMutex; + AIThreadID mLockingThread; +#ifdef NEEDS_MUTEX_RECURSION + S32 mLockDepth; +#endif + }; +#endif + +private: + void lock_main(LLFastTimer::DeclareTimer* timer); + + bool inc_lock_if_recursive() + { +#ifdef NEEDS_MUTEX_RECURSION + if (isSelfLocked()) + { + mLockDepth++; + return true; + } +#endif + return false; + } + + mutable AIThreadID mLockingThread; +#ifdef NEEDS_MUTEX_RECURSION + LLAtomicS32 mLockDepth; +#endif }; -#endif // APR_HAS_THREADS + +class LLGlobalMutex : public LLMutex +{ +public: + LLGlobalMutex() : LLMutex(MUTEX_POOL(LLAPRRootPool::get())), mbInitalized(true) + {} + bool isInitalized() const + { + return mbInitalized; + } +private: + bool mbInitalized; +}; + +#ifdef NEEDS_MUTEX_IMPL +class LL_COMMON_API LLConditionVariableImpl : private boost::noncopyable +{ + friend class LLCondition; + + typedef impl_cond_handle_type native_handle_type; + + LLConditionVariableImpl(MUTEX_POOL(native_pool_type& pool)); + virtual ~LLConditionVariableImpl(); + void notify_one(); + void notify_all(); + void wait(LLMutex& lock); + native_handle_type& native_handle() { return mConditionVariableImpl; } + + native_handle_type mConditionVariableImpl; + MUTEX_POOL(native_pool_type mPool); +}; +#endif + +typedef LLMutex LLMutexRootPool; // Actually a condition/mutex pair (since each condition needs to be associated with a mutex). -class LL_COMMON_API LLCondition : public LLMutex +class LLCondition : public LLConditionVariableImpl, public LLMutex { public: - LLCondition(LLAPRPool& parent = LLThread::tldata().mRootPool); - ~LLCondition(); - - void wait(); // blocks - void signal(); - void broadcast(); - -protected: - apr_thread_cond_t *mAPRCondp; + LLCondition(MUTEX_POOL(native_pool_type& pool = LLThread::tldata().mRootPool)) : + LLMutex(MUTEX_POOL(pool)), + LLConditionVariableImpl(MUTEX_POOL(pool)) + {} + ~LLCondition() + {} + void LL_COMMON_API wait() + { + if (AIThreadID::in_main_thread_inline()) + wait_main(); + else LLConditionVariableImpl::wait(*this); + } + void signal() { LLConditionVariableImpl::notify_one(); } + void broadcast() { LLConditionVariableImpl::notify_all(); } +private: + void wait_main(); //Cannot be inline. Uses internal fasttimer. }; -class LL_COMMON_API LLMutexLock +class LLMutexLock { public: - LLMutexLock(LLMutexBase* mutex) + LLMutexLock(LLMutex* mutex) { mMutex = mutex; - if(mMutex) mMutex->lock(); + lock(); + } + LLMutexLock(LLMutex& mutex) + { + mMutex = &mutex; + lock(); } ~LLMutexLock() { - if(mMutex) mMutex->unlock(); + if (mMutex) mMutex->unlock(); } private: - LLMutexBase* mMutex; + LL_COMMON_API void lock(); //Cannot be inline. Uses internal fasttimer. + LLMutex* mMutex; }; -class LL_COMMON_API AIRWLock +class AIRWLock { public: AIRWLock(LLAPRPool& parent = LLThread::tldata().mRootPool) : - mWriterWaitingMutex(parent), mNoHoldersCondition(parent), mHoldersCount(0), mWriterIsWaiting(false) { } + mWriterWaitingMutex(MUTEX_POOL(parent)), mNoHoldersCondition(MUTEX_POOL(parent)), mHoldersCount(0), mWriterIsWaiting(false) { } private: LLMutex mWriterWaitingMutex; //!< This mutex is locked while some writer is waiting for access. @@ -398,7 +567,7 @@ public: }; #if LL_DEBUG -class LL_COMMON_API AINRLock +class AINRLock { private: int read_locked; @@ -409,15 +578,15 @@ private: void accessed(void) const { - if (!mAccessed) - { - mAccessed = true; - mTheadID.reset(); - } - else - { - llassert_always(mTheadID.equals_current_thread()); - } + if (!mAccessed) + { + mAccessed = true; + mTheadID.reset(); + } + else + { + llassert_always(mTheadID.equals_current_thread()); + } } public: @@ -451,22 +620,29 @@ void LLThread::unlockData() // see llmemory.h for LLPointer<> definition -class LL_COMMON_API LLThreadSafeRefCount +class LLThreadSafeRefCount { private: LLThreadSafeRefCount(const LLThreadSafeRefCount&); // not implemented LLThreadSafeRefCount&operator=(const LLThreadSafeRefCount&); // not implemented protected: - virtual ~LLThreadSafeRefCount(); // use unref() - + virtual ~LLThreadSafeRefCount() // use unref() + { + if (mRef != 0) + { + llerrs << "deleting non-zero reference" << llendl; + } + } + public: - LLThreadSafeRefCount(); - + LLThreadSafeRefCount() : mRef(0) + {} + void ref() { - mRef++; - } + mRef++; + } void unref() { diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h index e3eb2bded..696e4a105 100644 --- a/indra/llmath/llcalcparser.h +++ b/indra/llmath/llcalcparser.h @@ -55,44 +55,53 @@ T max_glue(T a, T b) return std::max(a, b); } +template struct lazy_pow_ { - template - struct result { typedef X type; }; - - template - X operator()(X x, Y y) const + template struct result; + template + struct result + { + typedef T type; + }; + + T operator()(T x, T y) const { return std::pow(x, y); } }; - + +template struct lazy_ufunc_ { - template - struct result { typedef A1 type; }; - - template - A1 operator()(F f, A1 a1) const + template struct result; + template + struct result { - return f(a1); + typedef T type; + }; + + T operator()(T(*fn)(T), T x) const + { + return fn(x); } }; - + +template struct lazy_bfunc_ { - template - struct result { typedef A1 type; }; - - template - A1 operator()(F f, A1 a1, A2 a2) const + template struct result; + template + struct result { - return f(a1, a2); + typedef T type; + }; + double operator()(T(*fn)(T, T), T x, T y) const + { + return fn(x, y); } }; - -//} // end namespace anonymous - + template struct grammar : boost::spirit::qi::grammar< @@ -178,9 +187,9 @@ struct grammar using boost::spirit::qi::no_case; using boost::spirit::qi::_val; - boost::phoenix::function lazy_pow; - boost::phoenix::function lazy_ufunc; - boost::phoenix::function lazy_bfunc; + boost::phoenix::function< lazy_pow_ > lazy_pow; + boost::phoenix::function< lazy_ufunc_ > lazy_ufunc; + boost::phoenix::function< lazy_bfunc_ > lazy_bfunc; expression = term [_val = _1] diff --git a/indra/llmessage/aicurl.cpp b/indra/llmessage/aicurl.cpp index 99683e0c5..68328c394 100644 --- a/indra/llmessage/aicurl.cpp +++ b/indra/llmessage/aicurl.cpp @@ -1300,7 +1300,7 @@ static int const HTTP_REDIRECTS_DEFAULT = 16; // Singu note: I've seen up to 10 // This limit is only here to avoid a redirect loop (infinite redirections). LLChannelDescriptors const BufferedCurlEasyRequest::sChannels; -LLMutex BufferedCurlEasyRequest::sResponderCallbackMutex; +LLGlobalMutex BufferedCurlEasyRequest::sResponderCallbackMutex; bool BufferedCurlEasyRequest::sShuttingDown = false; AIAverage BufferedCurlEasyRequest::sHTTPBandwidth(25); diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index be3d90e7d..9af27a567 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -430,7 +430,7 @@ class BufferedCurlEasyRequest : public CurlEasyRequest { public: static LLChannelDescriptors const sChannels; // Channel object for mInput (channel out()) and mOutput (channel in()). - static LLMutex sResponderCallbackMutex; // Locked while calling back any overridden ResponderBase::finished and/or accessing sShuttingDown. + static LLGlobalMutex sResponderCallbackMutex; // Locked while calling back any overridden ResponderBase::finished and/or accessing sShuttingDown. static bool sShuttingDown; // If true, no additional calls to ResponderBase::finished will be made anymore. static AIAverage sHTTPBandwidth; // HTTP bandwidth usage of all services combined. diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index 19f190b6b..b7057fe11 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -1098,10 +1098,10 @@ void AICurlThread::wakeup_thread(bool stop_thread) if (stop_thread) mRunning = false; // Thread-safe because all other threads were already stopped. - // Note, we do not want this function to be blocking the calling thread; therefore we only use tryLock()s. + // Note, we do not want this function to be blocking the calling thread; therefore we only use try_lock()s. // Stop two threads running the following code concurrently. - if (!mWakeUpMutex.tryLock()) + if (!mWakeUpMutex.try_lock()) { // If we failed to obtain mWakeUpMutex then another thread is (or was) in AICurlThread::wakeup_thread, // or curl was holding the lock for a micro second at the start of process_commands. @@ -1115,7 +1115,7 @@ void AICurlThread::wakeup_thread(bool stop_thread) } // Try if curl thread is still awake and if so, pass the new commands directly. - if (mWakeUpFlagMutex.tryLock()) + if (mWakeUpFlagMutex.try_lock()) { mWakeUpFlag = true; mWakeUpFlagMutex.unlock(); diff --git a/indra/llmessage/llpumpio.cpp b/indra/llmessage/llpumpio.cpp index 588265f97..a2b63e8e1 100644 --- a/indra/llmessage/llpumpio.cpp +++ b/indra/llmessage/llpumpio.cpp @@ -56,7 +56,7 @@ // constants for poll timeout. if we are threading, we want to have a // longer poll timeout. -#if LL_THREADS_APR +#if LL_THREADS_PUMPIO static const S32 DEFAULT_POLL_TIMEOUT = 1000; #else static const S32 DEFAULT_POLL_TIMEOUT = 0; @@ -168,24 +168,21 @@ LLPumpIO::LLPumpIO(void) : mPollset(NULL), mPollsetClientID(0), mNextLock(0), +#if LL_THREADS_PUMPIO + mPool(), + LLMutex mChainsMutex(initPool()), + LLMutex mCallbackMutex(initPool()), +#endif mCurrentPoolReallocCount(0), - mChainsMutex(NULL), - mCallbackMutex(NULL), mCurrentChain(mRunningChains.end()) { - mCurrentChain = mRunningChains.end(); - - initialize(); +#if !LL_THREADS_PUMPIO + initPool(); +#endif } LLPumpIO::~LLPumpIO() { -#if LL_THREADS_APR - if (mChainsMutex) apr_thread_mutex_destroy(mChainsMutex); - if (mCallbackMutex) apr_thread_mutex_destroy(mCallbackMutex); -#endif - mChainsMutex = NULL; - mCallbackMutex = NULL; if(mPollset) { // lldebugs << "cleaning up pollset" << llendl; @@ -218,8 +215,8 @@ bool LLPumpIO::addChain(chain_t const& chain, F32 timeout) info.mChainLinks.push_back(link); } -#if LL_THREADS_APR - LLScopedLock lock(mChainsMutex); +#if LL_THREADS_PUMPIO + LLMutexLock lock(mChainsMutex); #endif mPendingChains.push_back(info); return true; @@ -258,8 +255,8 @@ bool LLPumpIO::addChain( break; } } -#if LL_THREADS_APR - LLScopedLock lock(mChainsMutex); +#if LL_THREADS_PUMPIO + LLMutexLock lock(mChainsMutex); #endif mPendingChains.push_back(info); return true; @@ -403,8 +400,8 @@ void LLPumpIO::clearLock(S32 key) // therefore won't be treading into deleted memory. I think we can // also clear the lock on the chain safely since the pump only // reads that value. -#if LL_THREADS_APR - LLScopedLock lock(mChainsMutex); +#if LL_THREADS_PUMPIO + LLMutexLock lock(mChainsMutex); #endif mClearLocks.insert(key); } @@ -469,8 +466,8 @@ void LLPumpIO::pump(const S32& poll_timeout) PUMP_DEBUG; if(true) { -#if LL_THREADS_APR - LLScopedLock lock(mChainsMutex); +#if LL_THREADS_PUMPIO + LLMutexLock lock(mChainsMutex); #endif // bail if this pump is paused. if(PAUSING == mState) @@ -738,8 +735,8 @@ void LLPumpIO::pump(const S32& poll_timeout) //bool LLPumpIO::respond(const chain_t& pipes) //{ -//#if LL_THREADS_APR -// LLScopedLock lock(mCallbackMutex); +//#if LL_THREADS_PUMPIO +// LLMutexLock lock(mCallbackMutex); //#endif // LLChainInfo info; // links_t links; @@ -752,8 +749,8 @@ bool LLPumpIO::respond(LLIOPipe* pipe) { if(NULL == pipe) return false; -#if LL_THREADS_APR - LLScopedLock lock(mCallbackMutex); +#if LL_THREADS_PUMPIO + LLMutexLock lock(mCallbackMutex); #endif LLChainInfo info; LLLinkInfo link; @@ -773,8 +770,8 @@ bool LLPumpIO::respond( if(!data) return false; if(links.empty()) return false; -#if LL_THREADS_APR - LLScopedLock lock(mCallbackMutex); +#if LL_THREADS_PUMPIO + LLMutexLock lock(mCallbackMutex); #endif // Add the callback response @@ -793,8 +790,8 @@ void LLPumpIO::callback() //llinfos << "LLPumpIO::callback()" << llendl; if(true) { -#if LL_THREADS_APR - LLScopedLock lock(mCallbackMutex); +#if LL_THREADS_PUMPIO + LLMutexLock lock(mCallbackMutex); #endif std::copy( mPendingCallbacks.begin(), @@ -821,8 +818,8 @@ void LLPumpIO::callback() void LLPumpIO::control(LLPumpIO::EControl op) { -#if LL_THREADS_APR - LLScopedLock lock(mChainsMutex); +#if LL_THREADS_PUMPIO + LLMutexLock lock(mChainsMutex); #endif switch(op) { @@ -838,14 +835,11 @@ void LLPumpIO::control(LLPumpIO::EControl op) } } -void LLPumpIO::initialize(void) +LLAPRPool& LLPumpIO::initPool() { - mPool.create(); -#if LL_THREADS_APR - // SJB: Windows defaults to NESTED and OSX defaults to UNNESTED, so use UNNESTED explicitly. - apr_thread_mutex_create(&mChainsMutex, APR_THREAD_MUTEX_UNNESTED, mPool()); - apr_thread_mutex_create(&mCallbackMutex, APR_THREAD_MUTEX_UNNESTED, mPool()); -#endif + if (!mPool) + mPool.create(); + return mPool; } void LLPumpIO::rebuildPollset() diff --git a/indra/llmessage/llpumpio.h b/indra/llmessage/llpumpio.h index 0d1387257..309792c80 100644 --- a/indra/llmessage/llpumpio.h +++ b/indra/llmessage/llpumpio.h @@ -42,7 +42,7 @@ #include "llrun.h" // Define this to enable use with the APR thread library. -//#define LL_THREADS_APR 1 +//#define LL_THREADS_PUMPIO 1 // some simple constants to help with timeouts extern const F32 DEFAULT_CHAIN_EXPIRY_SECS; @@ -382,16 +382,13 @@ protected: LLAPRPool mCurrentPool; S32 mCurrentPoolReallocCount; -#if LL_THREADS_APR - apr_thread_mutex_t* mChainsMutex; - apr_thread_mutex_t* mCallbackMutex; -#else - int* mChainsMutex; - int* mCallbackMutex; +#if LL_THREADS_PUMPIO + LLMutex mChainsMutex; + LLMutex mCallbackMutex; #endif protected: - void initialize(); + LLAPRPool& initPool(); current_chain_t removeRunningChain(current_chain_t& chain) ; /** From 2d0905a4a6497ef58252562a3790257a3aef6578 Mon Sep 17 00:00:00 2001 From: Drake Arconis Date: Wed, 10 Sep 2014 01:11:14 -0400 Subject: [PATCH 06/55] Fix for fmod studio 1.5 --- indra/llaudio/llaudioengine_fmodstudio.cpp | 9 ++------- indra/llaudio/lllistener_fmodstudio.cpp | 6 ------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index 9b5ccebc0..966455a46 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -314,7 +314,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) result = mSystem->setSoftwareChannels(num_channels + 2); Check_FMOD_Error(result,"FMOD::System::setSoftwareChannels"); - U32 fmod_flags = FMOD_INIT_NORMAL; + U32 fmod_flags = FMOD_INIT_NORMAL | FMOD_INIT_3D_RIGHTHANDED; if(mEnableProfiler) { fmod_flags |= FMOD_INIT_PROFILE_ENABLE; @@ -446,7 +446,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) FMOD_SPEAKERMODE speaker_mode; mSystem->getDSPBufferSize(&r_bufferlength, &r_numbuffers); mSystem->getSoftwareFormat(&r_samplerate, &speaker_mode, NULL); - mSystem->getDriverInfo(0, r_name, NULL, 255, NULL, NULL, &speaker_mode, NULL); + mSystem->getDriverInfo(0, r_name, 255, NULL, NULL, &speaker_mode, NULL); std::string speaker_mode_str = "unknown"; switch(speaker_mode) { @@ -937,12 +937,7 @@ bool LLAudioBufferFMODSTUDIO::loadWAV(const std::string& filename) exinfo.cbsize = sizeof(exinfo); exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_WAV; //Hint to speed up loading. // Load up the wav file into an fmod sample -#if LL_WINDOWS - FMOD_RESULT result = getSystem()->createSound((const char*)utf8str_to_utf16str(filename).c_str(), base_mode | FMOD_UNICODE, &exinfo, &mSoundp); -#else FMOD_RESULT result = getSystem()->createSound(filename.c_str(), base_mode, &exinfo, &mSoundp); -#endif - if (result != FMOD_OK) { // We failed to load the file for some reason. diff --git a/indra/llaudio/lllistener_fmodstudio.cpp b/indra/llaudio/lllistener_fmodstudio.cpp index f6ee7908b..e2074ce2d 100644 --- a/indra/llaudio/lllistener_fmodstudio.cpp +++ b/indra/llaudio/lllistener_fmodstudio.cpp @@ -88,12 +88,6 @@ void LLListener_FMODSTUDIO::orient(LLVector3 up, LLVector3 at) { LLListener::orient(up, at); - // Welcome to the transition between right and left - // (coordinate systems, that is) - // Leaving the at vector alone results in a L/R reversal - // since DX is left-handed and we (LL, OpenGL, OpenAL) are right-handed - at = -at; - mSystem->set3DListenerAttributes(0, NULL, NULL, (FMOD_VECTOR*)at.mV, (FMOD_VECTOR*)up.mV); } From 0493a91a42b5b50f094e2fb3e05bac43cb0f5bc1 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 11 Sep 2014 05:20:01 -0500 Subject: [PATCH 07/55] Boost 1.52 uses a completely different api for atomics. Not going to be supporting them. Also, fix llcalcparser in more old-boost compatible manner, and fix linux usage of apr_signal_description_get. --- indra/llcommon/llapp.cpp | 1 + indra/llcommon/llatomic.h | 2 +- indra/llcommon/llthread.h | 5 +-- indra/llmath/llcalcparser.h | 60 +++++++++++------------------- indra/newview/llmeshrepository.cpp | 2 +- 5 files changed, 27 insertions(+), 43 deletions(-) diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index c1f925428..8ae5141a1 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -66,6 +66,7 @@ bool windows_post_minidump_callback(const wchar_t* dump_path, #else # include # include // for fork() +# include "apr_signal.h" // for apr_signal_description_get(int signum) void setup_signals(); void default_unix_signal_handler(int signum, siginfo_t *info, void *); diff --git a/indra/llcommon/llatomic.h b/indra/llcommon/llatomic.h index 0cfe99c77..525deaa2a 100644 --- a/indra/llcommon/llatomic.h +++ b/indra/llcommon/llatomic.h @@ -46,7 +46,7 @@ //Prefer boost over stl over apr. -#if defined(USE_BOOST_ATOMIC) && (BOOST_VERSION >= 105200) +#if defined(USE_BOOST_ATOMIC) && (BOOST_VERSION >= 105300) #include "boost/atomic.hpp" template struct impl_atomic_type { typedef boost::atomic type; }; diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index a3f95e6d8..8dee66b34 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -210,7 +210,6 @@ typedef CONDITION_VARIABLE impl_cond_handle_type; //----APR specific------ #include "apr_thread_cond.h" #include "apr_thread_mutex.h" -#include "apr_signal.h" typedef LLAPRPool native_pool_type; typedef apr_thread_mutex_t* impl_mutex_handle_type; typedef apr_thread_cond_t* impl_cond_handle_type; @@ -662,10 +661,10 @@ private: // Simple responder for self destructing callbacks // Pure virtual class -class LL_COMMON_API LLResponder : public LLThreadSafeRefCount +class LLResponder : public LLThreadSafeRefCount { protected: - virtual ~LLResponder(); + virtual ~LLResponder() {} public: virtual void completed(bool success) = 0; }; diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h index 696e4a105..6e81605bb 100644 --- a/indra/llmath/llcalcparser.h +++ b/indra/llmath/llcalcparser.h @@ -54,54 +54,39 @@ T max_glue(T a, T b) { return std::max(a, b); } - -template -struct lazy_pow_ -{ - template struct result; - template - struct result - { - typedef T type; - }; - - T operator()(T x, T y) const - { - return std::pow(x, y); - } -}; - -template + +template struct lazy_ufunc_ { - template struct result; - template - struct result - { - typedef T type; - }; + typedef RT result_type; - T operator()(T(*fn)(T), T x) const + template + struct result { typedef RT type; }; + + template + RT operator()(F f, A1 a1) const { - return fn(x); + return f(a1); } }; -template +template struct lazy_bfunc_ { - template struct result; - template - struct result + typedef F32 result_type; + + template + struct result { typedef RT type; }; + + template + RT operator()(F f, A1 a1, A2 a2) const { - typedef T type; - }; - double operator()(T(*fn)(T, T), T x, T y) const - { - return fn(x, y); + return f(a1, a2); } }; +//} // end namespace anonymous + template struct grammar : boost::spirit::qi::grammar< @@ -187,10 +172,9 @@ struct grammar using boost::spirit::qi::no_case; using boost::spirit::qi::_val; - boost::phoenix::function< lazy_pow_ > lazy_pow; boost::phoenix::function< lazy_ufunc_ > lazy_ufunc; boost::phoenix::function< lazy_bfunc_ > lazy_bfunc; - + expression = term [_val = _1] >> *( ('+' >> term [_val += _1]) @@ -207,7 +191,7 @@ struct grammar factor = primary [_val = _1] - >> *( ("**" >> factor [_val = lazy_pow(_val, _1)]) + >> *( ("**" >> factor [_val = boost::phoenix::bind(static_cast(&std::pow),_val,_1)]) ) ; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 9f640b3f7..037c42664 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -2334,7 +2334,7 @@ void LLMeshRepository::notifyLoadedMeshes() //call completed callbacks on finished decompositions mDecompThread->notifyCompleted(); - if (!mThread->mSignal->tryLock()) + if (!mThread->mSignal->try_lock()) { //curl thread is churning, wait for it to go idle return; } From 738ce6422a83e6e0f6b6cce8dc356d8f4212e06b Mon Sep 17 00:00:00 2001 From: Shyotl Date: Mon, 15 Sep 2014 15:22:33 -0500 Subject: [PATCH 08/55] A bit of code consolidation. Unified code for OUT_TERSE_IMPROVED and OUT_FULL terse subset. --- indra/newview/llviewerobject.cpp | 493 +++++++++++-------------------- indra/newview/llviewerobject.h | 2 + 2 files changed, 179 insertions(+), 316 deletions(-) diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 9ea0c19d5..b140b01d2 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -984,26 +984,10 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, // Use getPosition, not getPositionRegion, since this is what we're comparing directly against. LLVector3 test_pos_parent = getPosition(); - U8 data[60+16]; // This needs to match the largest size below. -#ifdef LL_BIG_ENDIAN - U16 valswizzle[4]; -#endif - U16 *val; -// Aurora Sim - //const F32 size = LLWorld::getInstance()->getRegionWidthInMeters(); - const F32 size = mRegionp->getWidth(); -// Aurora Sim - const F32 MAX_HEIGHT = LLWorld::getInstance()->getRegionMaxHeight(); - const F32 MIN_HEIGHT = LLWorld::getInstance()->getRegionMinHeight(); - S32 length; - S32 count; S32 this_update_precision = 32; // in bits // Temporaries, because we need to compare w/ previous to set dirty flags... - LLVector3 new_pos_parent; - LLVector3 new_vel; - LLVector3 new_acc; - LLVector3 new_angv; + LLVector3 new_pos_parent, new_vel, new_angv; LLVector3 old_angv = getAngularVelocity(); LLQuaternion new_rot; LLVector3 new_scale = getScale(); @@ -1031,6 +1015,8 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, #ifdef DEBUG_UPDATE_TYPE llinfos << "Full:" << getID() << llendl; #endif + processTerseData(mesgsys, user_data, block_num, this_update_precision, new_pos_parent, new_rot, new_angv, test_pos_parent); + //clear cost and linkset cost mCostStale = true; if (isSelected()) @@ -1053,8 +1039,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_Material, material, block_num ); mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_ClickAction, click_action, block_num); mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_Scale, new_scale, block_num ); - length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ObjectData); - mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ObjectData, data, length, block_num); mTotalCRC = crc; @@ -1072,157 +1056,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, } setClickAction(click_action); - count = 0; - LLVector4 collision_plane; - - switch(length) - { - case (60 + 16): - // pull out collision normal for avatar - htonmemcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); - ((LLVOAvatar*)this)->setFootPlane(collision_plane); - count += sizeof(LLVector4); - // fall through - case 60: - this_update_precision = 32; - // this is a terse update - // pos - htonmemcpy(new_pos_parent.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - count += sizeof(LLVector3); - // vel - htonmemcpy((void*)getVelocity().mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - count += sizeof(LLVector3); - // acc - htonmemcpy((void*)getAcceleration().mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - count += sizeof(LLVector3); - // theta - { - LLVector3 vec; - htonmemcpy(vec.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - new_rot.unpackFromVector3(vec); - } - count += sizeof(LLVector3); - // omega - htonmemcpy((void*)new_angv.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - if (new_angv.isExactlyZero()) - { - // reset rotation time - resetRot(); - } - setAngularVelocity(new_angv); -#if LL_DARWIN - if (length == 76) - { - setAngularVelocity(LLVector3::zero); - } -#endif - break; - case(32 + 16): - // pull out collision normal for avatar - htonmemcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); - ((LLVOAvatar*)this)->setFootPlane(collision_plane); - count += sizeof(LLVector4); - // fall through - case 32: - this_update_precision = 16; - test_pos_parent.quantize16(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT); - - // This is a terse 16 update, so treat data as an array of U16's. -#ifdef LL_BIG_ENDIAN - htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*3; - new_pos_parent.mV[VX] = U16_to_F32(val[VX], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VY] = U16_to_F32(val[VY], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VZ] = U16_to_F32(val[VZ], MIN_HEIGHT, MAX_HEIGHT); - -#ifdef LL_BIG_ENDIAN - htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*3; - setVelocity(LLVector3(U16_to_F32(val[VX], -size, size), - U16_to_F32(val[VY], -size, size), - U16_to_F32(val[VZ], -size, size))); - -#ifdef LL_BIG_ENDIAN - htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*3; - setAcceleration(LLVector3(U16_to_F32(val[VX], -size, size), - U16_to_F32(val[VY], -size, size), - U16_to_F32(val[VZ], -size, size))); - -#ifdef LL_BIG_ENDIAN - htonmemcpy(valswizzle, &data[count], MVT_U16Quat, 4); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*4; - new_rot.mQ[VX] = U16_to_F32(val[VX], -1.f, 1.f); - new_rot.mQ[VY] = U16_to_F32(val[VY], -1.f, 1.f); - new_rot.mQ[VZ] = U16_to_F32(val[VZ], -1.f, 1.f); - new_rot.mQ[VW] = U16_to_F32(val[VW], -1.f, 1.f); - -#ifdef LL_BIG_ENDIAN - htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - new_angv.setVec(U16_to_F32(val[VX], -size, size), - U16_to_F32(val[VY], -size, size), - U16_to_F32(val[VZ], -size, size)); - if (new_angv.isExactlyZero()) - { - // reset rotation time - resetRot(); - } - setAngularVelocity(new_angv); - break; - - case 16: - this_update_precision = 8; - test_pos_parent.quantize8(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT); - // this is a terse 8 update - new_pos_parent.mV[VX] = U8_to_F32(data[0], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VY] = U8_to_F32(data[1], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VZ] = U8_to_F32(data[2], MIN_HEIGHT, MAX_HEIGHT); - - setVelocity(U8_to_F32(data[3], -size, size), - U8_to_F32(data[4], -size, size), - U8_to_F32(data[5], -size, size) ); - - setAcceleration(U8_to_F32(data[6], -size, size), - U8_to_F32(data[7], -size, size), - U8_to_F32(data[8], -size, size) ); - - new_rot.mQ[VX] = U8_to_F32(data[9], -1.f, 1.f); - new_rot.mQ[VY] = U8_to_F32(data[10], -1.f, 1.f); - new_rot.mQ[VZ] = U8_to_F32(data[11], -1.f, 1.f); - new_rot.mQ[VW] = U8_to_F32(data[12], -1.f, 1.f); - - new_angv.setVec(U8_to_F32(data[13], -size, size), - U8_to_F32(data[14], -size, size), - U8_to_F32(data[15], -size, size) ); - if (new_angv.isExactlyZero()) - { - // reset rotation time - resetRot(); - } - setAngularVelocity(new_angv); - break; - } - //////////////////////////////////////////////////// // // Here we handle data specific to the full message. @@ -1371,152 +1204,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, #ifdef DEBUG_UPDATE_TYPE llinfos << "TI:" << getID() << llendl; #endif - length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ObjectData); - mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ObjectData, data, length, block_num); - count = 0; - LLVector4 collision_plane; - - switch(length) - { - case(60 + 16): - // pull out collision normal for avatar - htonmemcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); - ((LLVOAvatar*)this)->setFootPlane(collision_plane); - count += sizeof(LLVector4); - // fall through - case 60: - // this is a terse 32 update - // pos - this_update_precision = 32; - htonmemcpy(new_pos_parent.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - count += sizeof(LLVector3); - // vel - htonmemcpy((void*)getVelocity().mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - count += sizeof(LLVector3); - // acc - htonmemcpy((void*)getAcceleration().mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - count += sizeof(LLVector3); - // theta - { - LLVector3 vec; - htonmemcpy(vec.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - new_rot.unpackFromVector3(vec); - } - count += sizeof(LLVector3); - // omega - htonmemcpy((void*)new_angv.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - if (new_angv.isExactlyZero()) - { - // reset rotation time - resetRot(); - } - setAngularVelocity(new_angv); -#if LL_DARWIN - if (length == 76) - { - setAngularVelocity(LLVector3::zero); - } -#endif - break; - case(32 + 16): - // pull out collision normal for avatar - htonmemcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); - ((LLVOAvatar*)this)->setFootPlane(collision_plane); - count += sizeof(LLVector4); - // fall through - case 32: - // this is a terse 16 update - this_update_precision = 16; - test_pos_parent.quantize16(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT); - -#ifdef LL_BIG_ENDIAN - htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*3; - new_pos_parent.mV[VX] = U16_to_F32(val[VX], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VY] = U16_to_F32(val[VY], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VZ] = U16_to_F32(val[VZ], MIN_HEIGHT, MAX_HEIGHT); - -#ifdef LL_BIG_ENDIAN - htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*3; - setVelocity(U16_to_F32(val[VX], -size, size), - U16_to_F32(val[VY], -size, size), - U16_to_F32(val[VZ], -size, size)); - -#ifdef LL_BIG_ENDIAN - htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*3; - setAcceleration(U16_to_F32(val[VX], -size, size), - U16_to_F32(val[VY], -size, size), - U16_to_F32(val[VZ], -size, size)); - -#ifdef LL_BIG_ENDIAN - htonmemcpy(valswizzle, &data[count], MVT_U16Quat, 8); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*4; - new_rot.mQ[VX] = U16_to_F32(val[VX], -1.f, 1.f); - new_rot.mQ[VY] = U16_to_F32(val[VY], -1.f, 1.f); - new_rot.mQ[VZ] = U16_to_F32(val[VZ], -1.f, 1.f); - new_rot.mQ[VW] = U16_to_F32(val[VW], -1.f, 1.f); - -#ifdef LL_BIG_ENDIAN - htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - new_angv.set(U16_to_F32(val[VX], -size, size), - U16_to_F32(val[VY], -size, size), - U16_to_F32(val[VZ], -size, size)); - setAngularVelocity(new_angv); - break; - - case 16: - // this is a terse 8 update - this_update_precision = 8; - test_pos_parent.quantize8(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT); - new_pos_parent.mV[VX] = U8_to_F32(data[0], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VY] = U8_to_F32(data[1], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VZ] = U8_to_F32(data[2], MIN_HEIGHT, MAX_HEIGHT); - - setVelocity(U8_to_F32(data[3], -size, size), - U8_to_F32(data[4], -size, size), - U8_to_F32(data[5], -size, size) ); - - setAcceleration(U8_to_F32(data[6], -size, size), - U8_to_F32(data[7], -size, size), - U8_to_F32(data[8], -size, size) ); - - new_rot.mQ[VX] = U8_to_F32(data[9], -1.f, 1.f); - new_rot.mQ[VY] = U8_to_F32(data[10], -1.f, 1.f); - new_rot.mQ[VZ] = U8_to_F32(data[11], -1.f, 1.f); - new_rot.mQ[VW] = U8_to_F32(data[12], -1.f, 1.f); - - new_angv.set(U8_to_F32(data[13], -size, size), - U8_to_F32(data[14], -size, size), - U8_to_F32(data[15], -size, size) ); - setAngularVelocity(new_angv); - break; - } - - U8 state; - mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num ); - mState = state; + processTerseData(mesgsys, user_data, block_num, this_update_precision, new_pos_parent, new_rot, new_angv, test_pos_parent); break; } @@ -2297,6 +1985,179 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, return retval; } +void LLViewerObject::processTerseData(LLMessageSystem *mesgsys, void **user_data, U32 block_num, S32& this_update_precision, LLVector3& new_pos_parent, LLQuaternion& new_rot, LLVector3& new_angv, LLVector3& test_pos_parent) +{ + // Aurora Sim + //const F32 size = LLWorld::getInstance()->getRegionWidthInMeters(); + const F32 size = mRegionp->getWidth(); + // Aurora Sim + const F32 MAX_HEIGHT = LLWorld::getInstance()->getRegionMaxHeight(); + const F32 MIN_HEIGHT = LLWorld::getInstance()->getRegionMinHeight(); + + U8 data[60+16]; // This needs to match the largest size below. +#ifdef LL_BIG_ENDIAN + U16 valswizzle[4]; +#endif + U16 *val; + S32 count = 0; + LLVector4 collision_plane; + + S32 length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ObjectData); + mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ObjectData, data, length, block_num); + + switch (length) + { + case(60 + 16) : + // pull out collision normal for avatar + htonmemcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); + ((LLVOAvatar*)this)->setFootPlane(collision_plane); + count += sizeof(LLVector4); + // fall through + case 60: + // this is a terse 32 update + // pos + this_update_precision = 32; + htonmemcpy(new_pos_parent.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); + count += sizeof(LLVector3); + // vel + htonmemcpy((void*)getVelocity().mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); + count += sizeof(LLVector3); + // acc + htonmemcpy((void*)getAcceleration().mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); + count += sizeof(LLVector3); + // theta + { + LLVector3 vec; + htonmemcpy(vec.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); + new_rot.unpackFromVector3(vec); + } + count += sizeof(LLVector3); + // omega + htonmemcpy((void*)new_angv.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); + if (new_angv.isExactlyZero()) + { + // reset rotation time + resetRot(); + } + setAngularVelocity(new_angv); +#if LL_DARWIN + if (length == 76) + { + setAngularVelocity(LLVector3::zero); + } +#endif + break; + case(32 + 16) : + // pull out collision normal for avatar + htonmemcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); + ((LLVOAvatar*)this)->setFootPlane(collision_plane); + count += sizeof(LLVector4); + // fall through + case 32: + // this is a terse 16 update + this_update_precision = 16; + test_pos_parent.quantize16(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT); + +#ifdef LL_BIG_ENDIAN + htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6); + val = valswizzle; +#else + val = (U16 *)&data[count]; +#endif + count += sizeof(U16) * 3; + new_pos_parent.mV[VX] = U16_to_F32(val[VX], -0.5f*size, 1.5f*size); + new_pos_parent.mV[VY] = U16_to_F32(val[VY], -0.5f*size, 1.5f*size); + new_pos_parent.mV[VZ] = U16_to_F32(val[VZ], MIN_HEIGHT, MAX_HEIGHT); + +#ifdef LL_BIG_ENDIAN + htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6); + val = valswizzle; +#else + val = (U16 *)&data[count]; +#endif + count += sizeof(U16) * 3; + setVelocity(U16_to_F32(val[VX], -size, size), + U16_to_F32(val[VY], -size, size), + U16_to_F32(val[VZ], -size, size)); + +#ifdef LL_BIG_ENDIAN + htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6); + val = valswizzle; +#else + val = (U16 *)&data[count]; +#endif + count += sizeof(U16) * 3; + setAcceleration(U16_to_F32(val[VX], -size, size), + U16_to_F32(val[VY], -size, size), + U16_to_F32(val[VZ], -size, size)); + +#ifdef LL_BIG_ENDIAN + htonmemcpy(valswizzle, &data[count], MVT_U16Quat, 8); + val = valswizzle; +#else + val = (U16 *)&data[count]; +#endif + count += sizeof(U16) * 4; + new_rot.mQ[VX] = U16_to_F32(val[VX], -1.f, 1.f); + new_rot.mQ[VY] = U16_to_F32(val[VY], -1.f, 1.f); + new_rot.mQ[VZ] = U16_to_F32(val[VZ], -1.f, 1.f); + new_rot.mQ[VW] = U16_to_F32(val[VW], -1.f, 1.f); + +#ifdef LL_BIG_ENDIAN + htonmemcpy(valswizzle, &data[count], MVT_U16Vec3, 6); + val = valswizzle; +#else + val = (U16 *)&data[count]; +#endif + new_angv.set(U16_to_F32(val[VX], -size, size), + U16_to_F32(val[VY], -size, size), + U16_to_F32(val[VZ], -size, size)); + if (new_angv.isExactlyZero()) + { + // reset rotation time + resetRot(); + } + setAngularVelocity(new_angv); + break; + + case 16: + // this is a terse 8 update + this_update_precision = 8; + test_pos_parent.quantize8(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT); + new_pos_parent.mV[VX] = U8_to_F32(data[0], -0.5f*size, 1.5f*size); + new_pos_parent.mV[VY] = U8_to_F32(data[1], -0.5f*size, 1.5f*size); + new_pos_parent.mV[VZ] = U8_to_F32(data[2], MIN_HEIGHT, MAX_HEIGHT); + + setVelocity(U8_to_F32(data[3], -size, size), + U8_to_F32(data[4], -size, size), + U8_to_F32(data[5], -size, size)); + + setAcceleration(U8_to_F32(data[6], -size, size), + U8_to_F32(data[7], -size, size), + U8_to_F32(data[8], -size, size)); + + new_rot.mQ[VX] = U8_to_F32(data[9], -1.f, 1.f); + new_rot.mQ[VY] = U8_to_F32(data[10], -1.f, 1.f); + new_rot.mQ[VZ] = U8_to_F32(data[11], -1.f, 1.f); + new_rot.mQ[VW] = U8_to_F32(data[12], -1.f, 1.f); + + new_angv.set(U8_to_F32(data[13], -size, size), + U8_to_F32(data[14], -size, size), + U8_to_F32(data[15], -size, size)); + if (new_angv.isExactlyZero()) + { + // reset rotation time + resetRot(); + } + setAngularVelocity(new_angv); + break; + default: + break; + } + + mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, mState, block_num); +} + BOOL LLViewerObject::isActive() const { return TRUE; diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 40564be40..d0f86260f 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -168,6 +168,8 @@ public: const EObjectUpdateType update_type, LLDataPacker *dp); + void processTerseData(LLMessageSystem *mesgsys, void **user_data, U32 block_num, S32& this_update_precision, LLVector3& new_pos_parent, LLQuaternion& new_rot, LLVector3& new_angv, LLVector3& test_pos_parent); + virtual BOOL isActive() const; // Whether this object needs to do an idleUpdate. BOOL onActiveList() const {return mOnActiveList;} From f6209dec3486aa6de105048145d54d306f2a2c46 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Mon, 15 Sep 2014 15:23:22 -0500 Subject: [PATCH 09/55] Silence some LL_INFOS spam on shader init. --- indra/llrender/llshadermgr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 37a16ff6f..7181f1e05 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -536,7 +536,7 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade { if((*it).second.mLevel == shader_level && (*it).second.mType == type && (*it).second.mDefinitions == (defines ? *defines : std::map())) { - llinfos << "Loading cached shader for " << filename << llendl; + //LL_INFOS("ShaderLoading") << "Loading cached shader for " << filename << LL_ENDL; return (*it).second.mHandle; } } @@ -575,7 +575,7 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade file = LLFile::fopen(fname.str(), "r"); /* Flawfinder: ignore */ if (file) { - LL_INFOS("ShaderLoading") << "Loading file: shaders/class" << gpu_class << "/" << filename << " (Want class " << gpu_class << ")" << LL_ENDL; + //LL_INFOS("ShaderLoading") << "Loading file: shaders/class" << gpu_class << "/" << filename << " (Want class " << gpu_class << ")" << LL_ENDL; break; // done } } From 972c92a3fe60aef1a162c46b4d930cef07f4a26f Mon Sep 17 00:00:00 2001 From: Shyotl Date: Mon, 15 Sep 2014 15:24:40 -0500 Subject: [PATCH 10/55] Inconsequential template tweak. --- indra/llmath/llcalcparser.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h index 6e81605bb..8df507bc8 100644 --- a/indra/llmath/llcalcparser.h +++ b/indra/llmath/llcalcparser.h @@ -73,7 +73,7 @@ struct lazy_ufunc_ template struct lazy_bfunc_ { - typedef F32 result_type; + typedef RT result_type; template struct result { typedef RT type; }; From 6e537cd32261f0bc9bbe68204297f5a7f45e852d Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sat, 22 Nov 2014 22:01:45 -0600 Subject: [PATCH 11/55] Try to handle BOM for utf stream metadata (doesn't seem common, at all). --- indra/llaudio/llstreamingaudio_fmodex.cpp | 78 +++++++++++++++++++---- 1 file changed, 64 insertions(+), 14 deletions(-) diff --git a/indra/llaudio/llstreamingaudio_fmodex.cpp b/indra/llaudio/llstreamingaudio_fmodex.cpp index 9e3e406c7..be5888aa5 100644 --- a/indra/llaudio/llstreamingaudio_fmodex.cpp +++ b/indra/llaudio/llstreamingaudio_fmodex.cpp @@ -127,6 +127,45 @@ void LLStreamingAudio_FMODEX::start(const std::string& url) } } +enum utf_endian_type_t +{ + UTF16LE, + UTF16BE, + UTF16 +}; + +std::string utf16input_to_utf8(char* input, U32 len, utf_endian_type_t type) +{ + if (type == UTF16) + { + type = UTF16BE; //Default + if (len > 2) + { + //Parse and strip BOM. + if ((input[0] == 0xFE && input[1] == 0xFF) || + (input[0] == 0xFF && input[1] == 0xFE)) + { + input += 2; + len -= 2; + type = input[0] == 0xFE ? UTF16BE : UTF16LE; + } + } + } + llutf16string out_16((U16*)input, len / 2); + if (len % 2) + { + out_16.push_back((input)[len - 1] << 8); + } + if (type == UTF16BE) + { + for (llutf16string::iterator i = out_16.begin(); i < out_16.end(); ++i) + { + llutf16string::value_type v = *i; + *i = ((v & 0x00FF) << 8) | ((v & 0xFF00) >> 8); + } + } + return utf16str_to_utf8str(out_16); +} void LLStreamingAudio_FMODEX::update() { @@ -216,23 +255,27 @@ void LLStreamingAudio_FMODEX::update() switch(tag.type) //Crappy tag translate table. { case(FMOD_TAGTYPE_ID3V2): - if(name == "TIT2") name = "TITLE"; + if (!LLStringUtil::compareInsensitive(name, "TIT2")) name = "TITLE"; else if(name == "TPE1") name = "ARTIST"; break; case(FMOD_TAGTYPE_ASF): - if(name == "Title") name = "TITLE"; - else if(name == "WM/AlbumArtist") name = "ARTIST"; + if (!LLStringUtil::compareInsensitive(name, "Title")) name = "TITLE"; + else if (!LLStringUtil::compareInsensitive(name, "WM/AlbumArtist")) name = "ARTIST"; break; case(FMOD_TAGTYPE_FMOD): - if (!strcmp(tag.name, "Sample Rate Change")) + if (!LLStringUtil::compareInsensitive(name, "Sample Rate Change")) { llinfos << "Stream forced changing sample rate to " << *((float *)tag.data) << llendl; mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data)); } continue; default: + if (!LLStringUtil::compareInsensitive(name, "TITLE") || + !LLStringUtil::compareInsensitive(name, "ARTIST")) + LLStringUtil::toUpper(name); break; } + switch(tag.datatype) { case(FMOD_TAGDATATYPE_INT): @@ -247,24 +290,31 @@ void LLStreamingAudio_FMODEX::update() { std::string out = rawstr_to_utf8(std::string((char*)tag.data,tag.datalen)); (*mMetaData)[name]=out; - llinfos << tag.name << ": " << out << llendl; + llinfos << tag.name << "(RAW): " << out << llendl; + } + break; + case(FMOD_TAGDATATYPE_STRING_UTF8) : + { + U8 offs = 0; + if (tag.datalen > 3 && ((char*)tag.data)[0] == 0xEF && ((char*)tag.data)[1] == 0xBB && ((char*)tag.data)[2] == 0xBF) + offs = 3; + std::string out((char*)tag.data + offs, tag.datalen - offs); + (*mMetaData)[name] = out; + llinfos << tag.name << "(UTF8): " << out << llendl; } break; case(FMOD_TAGDATATYPE_STRING_UTF16): { - std::string out((char*)tag.data,tag.datalen); - (*mMetaData)[std::string(tag.name)]=out; - llinfos << tag.name << ": " << out << llendl; + std::string out = utf16input_to_utf8((char*)tag.data, tag.datalen, UTF16); + (*mMetaData)[name] = out; + llinfos << tag.name << "(UTF16): " << out << llendl; } break; case(FMOD_TAGDATATYPE_STRING_UTF16BE): { - std::string out((char*)tag.data,tag.datalen); - U16* buf = (U16*)out.c_str(); - for(U32 j = 0; j < out.size()/2; ++j) - (((buf[j] & 0xff)<<8) | ((buf[j] & 0xff00)>>8)); - (*mMetaData)[std::string(tag.name)]=out; - llinfos << tag.name << ": " << out << llendl; + std::string out = utf16input_to_utf8((char*)tag.data, tag.datalen, UTF16BE); + (*mMetaData)[name] = out; + llinfos << tag.name << "(UTF16BE): " << out << llendl; } default: break; From be9d4177785b596d37cff4f57b3ce13a881baced Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sat, 22 Nov 2014 22:02:20 -0600 Subject: [PATCH 12/55] Added vector_shrink_to_fit to llstl.h --- indra/llcommon/llstl.h | 13 +++++++++++++ indra/llmath/lloctree.h | 9 +-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h index 5afd5dc61..fdd2f7f22 100644 --- a/indra/llcommon/llstl.h +++ b/indra/llcommon/llstl.h @@ -287,6 +287,19 @@ inline T* vector_append(std::vector& invec, S32 N) return &(invec[sz]); } +template +inline void vector_shrink_to_fit(std::vector& invec) +{ + //For Windows: We always assume vs2010 or later, which support this c++11 feature with no configuration needed. + //For GCC: __cplusplus >= 201103L indicates C++11 support. __GXX_EXPERIMENTAL_CXX0X being set indicates experimental c++0x support. C++11 support replaces C++0x support. + // std::vector::shrink_to_fit was added to GCCs C++0x implementation in version 4.5.0. +#if defined(LL_WINDOWS) || __cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X) && __GNUC_MINOR__ >= 5) + invec.shrink_to_fit(); +#else + invec(invec.begin(), invec.end()).swap(invec); +#endif +} + // call function f to n members starting at first. similar to std::for_each template Function ll_for_n(InputIter first, Size n, Function f) diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h index 0be162f5c..25bec541c 100644 --- a/indra/llmath/lloctree.h +++ b/indra/llmath/lloctree.h @@ -696,14 +696,7 @@ public: (mData.size() > gOctreeReserveCapacity && mData.capacity() > gOctreeReserveCapacity + mData.size() - 1 - (mData.size() - gOctreeReserveCapacity - 1) % 4)) { //Shrink to lowest possible (reserve)+4*i size.. Say reserve is 5, here are [size,capacity] pairs. [10,13],[9,9],[8,9],[7,9],[6,9],[5,5],[4,5],[3,5],[2,5],[1,5],[0,5] - //For Windows: We always assume vs2010 or later, which support this c++11 feature with no configuration needed. - //For GCC: __cplusplus >= 201103L indicates C++11 support. __GXX_EXPERIMENTAL_CXX0X being set indicates experimental c++0x support. C++11 support replaces C++0x support. - // std::vector::shrink_to_fit was added to GCCs C++0x implementation in version 4.5.0. -#if defined(LL_WINDOWS) || __cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X) && __GNUC_MINOR__ >= 5) - mData.shrink_to_fit(); -#else - std::vector >(mData.begin(), mData.end()).swap(mData); //Need to confirm this works on OSX.. -#endif + vector_shrink_to_fit(mData); } #ifdef LL_OCTREE_STATS if(old_cap != mData.capacity()) From f2f8ecab98dba833f295a90732f8f619861e7d81 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sat, 22 Nov 2014 22:02:46 -0600 Subject: [PATCH 13/55] Clean up code for vivox session participant management. --- indra/newview/llvoicevivox.cpp | 191 ++++++++++++--------------------- indra/newview/llvoicevivox.h | 16 +-- 2 files changed, 80 insertions(+), 127 deletions(-) diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 46edb4fa7..2ee1c17bd 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -2582,14 +2582,15 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void) if(mAudioSession && (mAudioSession->mVolumeDirty || mAudioSession->mMuteDirty)) { - participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); + // Singu Note: mParticipantList has replaced mParticipantsByURI. + participantList::iterator iter = mAudioSession->mParticipantList.begin(); mAudioSession->mVolumeDirty = false; mAudioSession->mMuteDirty = false; - for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) + for(; iter != mAudioSession->mParticipantList.end(); iter++) { - participantState *p = iter->second; + participantState *p = &*iter; if(p->mVolumeDirty) { @@ -2616,7 +2617,7 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void) mute = true; } - LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL; + LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute ? "/true" : "/false") << LL_ENDL; // SLIM SDK: Send both volume and mute commands. @@ -3449,15 +3450,8 @@ void LLVivoxVoiceClient::participantRemovedEvent( sessionState *session = findSession(sessionHandle); if(session) { - participantState *participant = session->findParticipant(uriString); - if(participant) - { - session->removeParticipant(participant); - } - else - { - LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL; - } + // Singu Note: removeParticipant now internally finds the entry. More efficient. Other option is to add a find procedure that returns an iterator. + session->removeParticipant(uriString); } else { @@ -3753,14 +3747,13 @@ void LLVivoxVoiceClient::muteListChanged() // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. if(mAudioSession) { - participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); + // Singu Note: mParticipantList has replaced mParticipantsByURI. + participantList::iterator iter = mAudioSession->mParticipantList.begin(); - for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) + for(; iter != mAudioSession->mParticipantList.end(); iter++) { - participantState *p = iter->second; - // Check to see if this participant is on the mute list already - if(p->updateMuteState()) + if(iter->updateMuteState()) mAudioSession->mVolumeDirty = true; } } @@ -3768,7 +3761,8 @@ void LLVivoxVoiceClient::muteListChanged() ///////////////////////////// // Managing list of participants -LLVivoxVoiceClient::participantState::participantState(const std::string &uri) : +// Singu Note: Extended the ctor. +LLVivoxVoiceClient::participantState::participantState(const std::string &uri, const LLUUID& id, bool isAv) : mURI(uri), mPTT(false), mIsSpeaking(false), @@ -3780,69 +3774,51 @@ LLVivoxVoiceClient::participantState::participantState(const std::string &uri) : mOnMuteList(false), mVolumeSet(false), mVolumeDirty(false), - mAvatarIDValid(false), - mIsSelf(false) + mAvatarIDValid(isAv), + mIsSelf(false), + mAvatarID(id) { } LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParticipant(const std::string &uri) { - participantState *result = NULL; - bool useAlternateURI = false; + // Singu Note: If findParticipant returns non-null then alt uri stuff doesn't matter. Prior LL code was silly. + // Additonally, mParticipantList has replaced both mParticipantsByURI and mParticipantsByUUID, meaning we don't have two maps to maintain any longer. + + if (participantState* p = findParticipant(uri)) + return p; - // Note: this is mostly the body of LLVivoxVoiceClient::sessionState::findParticipant(), but since we need to know if it - // matched the alternate SIP URI (so we can add it properly), we need to reproduce it here. - { - participantMap::iterator iter = mParticipantsByURI.find(uri); + const std::string& desired_uri = (!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) ? mSIPURI : uri; - if(iter == mParticipantsByURI.end()) - { - if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) - { - // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. - // Use mSIPURI instead, since it will be properly encoded. - iter = mParticipantsByURI.find(mSIPURI); - useAlternateURI = true; - } - } - - if(iter != mParticipantsByURI.end()) - { - result = iter->second; - } - } - - if(!result) { // participant isn't already in one list or the other. - result = new participantState(useAlternateURI?mSIPURI:uri); - mParticipantsByURI.insert(participantMap::value_type(result->mURI, result)); mParticipantsChanged = true; // Try to do a reverse transform on the URI to get the GUID back. { LLUUID id; - if(LLVivoxVoiceClient::getInstance()->IDFromName(result->mURI, id)) + if (LLVivoxVoiceClient::getInstance()->IDFromName(desired_uri, id)) { - result->mAvatarIDValid = true; - result->mAvatarID = id; + mParticipantList.push_back(participantState(desired_uri, id, true)); } else { // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. // This indicates that the ID will not be in the name cache. - result->mAvatarID.generate(uri); + id.generate(uri); + mParticipantList.push_back(participantState(desired_uri, id, false)); } } + participantState* result = &mParticipantList.back(); - if(result->updateMuteState()) + if (result->updateMuteState()) { mMuteDirty = true; } - mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result)); - + // Singu Note: mParticipantsByUUID is dead. Keep it that way. + if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume)) { result->mVolumeDirty = true; @@ -3850,9 +3826,9 @@ LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParti } LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; - } - return result; + return result; + } } bool LLVivoxVoiceClient::participantState::updateMuteState() @@ -3876,35 +3852,25 @@ bool LLVivoxVoiceClient::participantState::isAvatar() return mAvatarIDValid; } -void LLVivoxVoiceClient::sessionState::removeParticipant(LLVivoxVoiceClient::participantState *participant) +// Singu Note: The only real necessary call to this immediatley followed a findParticipant call. +// Thus, just do the lookup here. It's faster to find the iterator only one time. +// Additonally, mParticipantList has replaced both mParticipantsByURI and mParticipantsByUUID, meaning we don't have two maps to maintain any longer. +void LLVivoxVoiceClient::sessionState::removeParticipant(const std::string& uri) { - if(participant) + participantList::iterator iter = std::find_if(mParticipantList.begin(), mParticipantList.end(), boost::bind(&participantList::value_type::mURI, _1) == uri); + if (iter != mParticipantList.end()) { - participantMap::iterator iter = mParticipantsByURI.find(participant->mURI); - participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(participant->mAvatarID); - - LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL; - - if(iter == mParticipantsByURI.end()) + vector_replace_with_last(mParticipantList, iter); + if (mParticipantList.empty() || mParticipantList.capacity() - mParticipantList.size() > 16) { - LL_ERRS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL; - } - else if(iter2 == mParticipantsByUUID.end()) - { - LL_ERRS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; - } - else if(iter->second != iter2->second) - { - LL_ERRS("Voice") << "Internal error: participant mismatch!" << LL_ENDL; - } - else - { - mParticipantsByURI.erase(iter); - mParticipantsByUUID.erase(iter2); - - delete participant; - mParticipantsChanged = true; + vector_shrink_to_fit(mParticipantList); } + mParticipantsChanged = true; + LL_DEBUGS("Voice") << "participant \"" << uri << "\" (" << iter->mAvatarID << ") removed." << LL_ENDL; + } + else + { + LL_DEBUGS("Voice") << "unknown participant " << uri << LL_ENDL; } } @@ -3912,87 +3878,72 @@ void LLVivoxVoiceClient::sessionState::removeAllParticipants() { LL_DEBUGS("Voice") << "called" << LL_ENDL; - while(!mParticipantsByURI.empty()) - { - removeParticipant(mParticipantsByURI.begin()->second); - } - - if(!mParticipantsByUUID.empty()) - { - LL_ERRS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL; - } + // Singu Note: mParticipantList has replaced both mParticipantsByURI and mParticipantsByUUID, meaning we don't have two maps to maintain any longer. + mParticipantList.clear(); + vector_shrink_to_fit(mParticipantList); } void LLVivoxVoiceClient::getParticipantList(std::set &participants) { if(mAudioSession) { - for(participantUUIDMap::iterator iter = mAudioSession->mParticipantsByUUID.begin(); - iter != mAudioSession->mParticipantsByUUID.end(); + // Singu Note: mParticipantList has replaced mParticipantsByURI. + for (participantList::iterator iter = mAudioSession->mParticipantList.begin(); + iter != mAudioSession->mParticipantList.end(); iter++) { - participants.insert(iter->first); + participants.insert(iter->mAvatarID); } } } bool LLVivoxVoiceClient::isParticipant(const LLUUID &speaker_id) { - if(mAudioSession) - { - return (mAudioSession->mParticipantsByUUID.find(speaker_id) != mAudioSession->mParticipantsByUUID.end()); - } - return false; + // Singu Note: findParticipantByID does the same thing. If null, returns false. + return findParticipantByID(speaker_id); } - LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::findParticipant(const std::string &uri) { - participantState *result = NULL; + // Singu Note: mParticipantList has replaced mParticipantsByURI. + participantList& vec = mParticipantList; + participantList::iterator iter = std::find_if(vec.begin(), vec.end(), boost::bind(&participantList::value_type::mURI, _1) == uri); - participantMap::iterator iter = mParticipantsByURI.find(uri); - - if(iter == mParticipantsByURI.end()) + if(iter == vec.end()) { if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) { // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. // Look up the other URI - iter = mParticipantsByURI.find(mSIPURI); + iter = std::find_if(vec.begin(), vec.end(), boost::bind(&participantList::value_type::mURI, _1) == mSIPURI); } } - if(iter != mParticipantsByURI.end()) + if (iter != mParticipantList.end()) { - result = iter->second; + return &*iter; } - return result; + return NULL; } LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::sessionState::findParticipantByID(const LLUUID& id) { - participantState * result = NULL; - participantUUIDMap::iterator iter = mParticipantsByUUID.find(id); + // Singu Note: mParticipantList has replaced mParticipantsByUUID. + participantList& vec = mParticipantList; + participantList::iterator iter = std::find_if(vec.begin(), vec.end(), boost::bind(&participantList::value_type::mAvatarID, _1) == id); - if(iter != mParticipantsByUUID.end()) + if(iter != mParticipantList.end()) { - result = iter->second; + return &*iter; } - return result; + return NULL; } LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::findParticipantByID(const LLUUID& id) { - participantState * result = NULL; - - if(mAudioSession) - { - result = mAudioSession->findParticipantByID(id); - } - - return result; + return mAudioSession ? mAudioSession->findParticipantByID(id) : NULL; } diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 3843bbd7e..a2afe4e66 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -246,7 +246,8 @@ protected: struct participantState { public: - participantState(const std::string &uri); + // Singu Note: Extended the ctor. + participantState(const std::string &uri, const LLUUID& id, bool isAv); bool updateMuteState(); // true if mute state has changed bool isAvatar(); @@ -270,9 +271,9 @@ protected: bool mAvatarIDValid; bool mIsSelf; }; - - typedef std::map participantMap; - typedef std::map participantUUIDMap; + + // Singu Note: participantList has replaced both participantMap and participantUUIDMap. + typedef std::vector participantList; struct sessionState { @@ -283,7 +284,8 @@ protected: participantState *addParticipant(const std::string &uri); // Note: after removeParticipant returns, the participant* that was passed to it will have been deleted. // Take care not to use the pointer again after that. - void removeParticipant(participantState *participant); + // Singu Note: removeParticipant now internally finds the entry. More efficient. Other option is to add a find procedure that returns an iterator. + void removeParticipant(const std::string& uri); void removeAllParticipants(); participantState *findParticipant(const std::string &uri); @@ -325,8 +327,8 @@ protected: bool mMuteDirty; bool mParticipantsChanged; - participantMap mParticipantsByURI; - participantUUIDMap mParticipantsByUUID; + // Singu Note: mParticipantList has replaced both mParticipantsByURI and mParticipantsByUUID. + participantList mParticipantList; LLUUID mVoiceFontID; }; From 7054a2a6d233c949f65eb5b5016c1a4a54d2cfb8 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sat, 22 Nov 2014 22:04:18 -0600 Subject: [PATCH 14/55] Added some fasttimers to LLVOAvatar::idleUpdate --- indra/newview/llvoavatar.cpp | 51 +++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 802105235..d734b5a68 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -2425,6 +2425,10 @@ S32 LLVOAvatar::setTETexture(const U8 te, const LLUUID& uuid) static LLFastTimer::DeclareTimer FTM_AVATAR_UPDATE("Avatar Update"); static LLFastTimer::DeclareTimer FTM_JOINT_UPDATE("Update Joints"); +static LLFastTimer::DeclareTimer FTM_CHARACTER_UPDATE("Character Update"); +static LLFastTimer::DeclareTimer FTM_BASE_UPDATE("Base Update"); +static LLFastTimer::DeclareTimer FTM_MISC_UPDATE("Misc Update"); +static LLFastTimer::DeclareTimer FTM_DETAIL_UPDATE("Detail Update"); //------------------------------------------------------------------------ // LLVOAvatar::dumpAnimationState() @@ -2510,7 +2514,10 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) if (isSelf()) { - LLViewerObject::idleUpdate(agent, world, time); + { + LLFastTimer t(FTM_BASE_UPDATE); + LLViewerObject::idleUpdate(agent, world, time); + } // trigger fidget anims if (isAnyAnimationSignaled(AGENT_STAND_ANIMS, NUM_AGENT_STAND_ANIMS)) @@ -2522,7 +2529,10 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { // Should override the idleUpdate stuff and leave out the angular update part. LLQuaternion rotation = getRotation(); - LLViewerObject::idleUpdate(agent, world, time); + { + LLFastTimer t(FTM_BASE_UPDATE); + LLViewerObject::idleUpdate(agent, world, time); + } setRotation(rotation); } @@ -2532,8 +2542,11 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) // animate the character // store off last frame's root position to be consistent with camera position LLVector3 root_pos_last = mRoot->getWorldPosition(); - bool detailed_update = updateCharacter(agent); - + bool detailed_update; + { + LLFastTimer t(FTM_CHARACTER_UPDATE); + detailed_update = updateCharacter(agent); + } if (gNoRender) { return; @@ -2543,19 +2556,23 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) bool voice_enabled = (visualizers_in_calls || LLVoiceClient::getInstance()->inProximalChannel()) && LLVoiceClient::getInstance()->getVoiceEnabled(mID); - idleUpdateVoiceVisualizer( voice_enabled ); - idleUpdateMisc( detailed_update ); - idleUpdateAppearanceAnimation(); - if (detailed_update) { - idleUpdateLipSync( voice_enabled ); - idleUpdateLoadingEffect(); - idleUpdateBelowWater(); // wind effect uses this - idleUpdateWindEffect(); - } + LLFastTimer t(FTM_MISC_UPDATE); + idleUpdateVoiceVisualizer(voice_enabled); + idleUpdateMisc(detailed_update); + idleUpdateAppearanceAnimation(); + if (detailed_update) + { + LLFastTimer t(FTM_DETAIL_UPDATE); + idleUpdateLipSync(voice_enabled); + idleUpdateLoadingEffect(); + idleUpdateBelowWater(); // wind effect uses this + idleUpdateWindEffect(); + } - idleUpdateNameTag( root_pos_last ); - idleUpdateRenderCost(); + idleUpdateNameTag(root_pos_last); + idleUpdateRenderCost(); + } } void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) @@ -5171,8 +5188,10 @@ void LLVOAvatar::releaseOldTextures() mTextureIDs = new_texture_ids; } +static LLFastTimer::DeclareTimer FTM_TEXTURE_UPDATE("Update Textures"); void LLVOAvatar::updateTextures() { + LLFastTimer t(FTM_TEXTURE_UPDATE); releaseOldTextures(); BOOL render_avatar = TRUE; @@ -6167,8 +6186,10 @@ BOOL LLVOAvatar::isActive() const //----------------------------------------------------------------------------- // setPixelAreaAndAngle() //----------------------------------------------------------------------------- +static LLFastTimer::DeclareTimer FTM_PIXEL_AREA("Pixel Area"); void LLVOAvatar::setPixelAreaAndAngle(LLAgent &agent) { + LLFastTimer t(FTM_PIXEL_AREA); if (mDrawable.isNull()) { return; From b3d86e626bfb52e75f4dd53bc55c7c7586cc0214 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 25 Nov 2014 16:32:04 -0600 Subject: [PATCH 15/55] Fixed wind dsp and implemented a DSP that mimics removed FMOD::Channel::getWaveData func. Fixed crash if fmod profiling was enabled. Fixed crash due to failing to check if resulting utf string from stream metadata was zero-length before calling std::string::back. --- indra/llaudio/llaudioengine_fmodex.cpp | 12 +- indra/llaudio/llaudioengine_fmodstudio.cpp | 84 ++++--- indra/llaudio/llstreamingaudio_fmodex.cpp | 8 + indra/llaudio/llstreamingaudio_fmodstudio.cpp | 232 ++++++++++++++---- indra/llaudio/llstreamingaudio_fmodstudio.h | 9 +- 5 files changed, 262 insertions(+), 83 deletions(-) diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index 972725171..c3e067c59 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -321,10 +321,6 @@ bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) if(mEnableProfiler) { fmod_flags |= FMOD_INIT_ENABLE_PROFILE; - mSystem->createChannelGroup("None", &mChannelGroups[AUDIO_TYPE_NONE]); - mSystem->createChannelGroup("SFX", &mChannelGroups[AUDIO_TYPE_SFX]); - mSystem->createChannelGroup("UI", &mChannelGroups[AUDIO_TYPE_UI]); - mSystem->createChannelGroup("Ambient", &mChannelGroups[AUDIO_TYPE_AMBIENT]); } #if LL_LINUX @@ -437,6 +433,14 @@ bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) return false; #endif + if (mEnableProfiler) + { + mSystem->createChannelGroup("None", &mChannelGroups[AUDIO_TYPE_NONE]); + mSystem->createChannelGroup("SFX", &mChannelGroups[AUDIO_TYPE_SFX]); + mSystem->createChannelGroup("UI", &mChannelGroups[AUDIO_TYPE_UI]); + mSystem->createChannelGroup("Ambient", &mChannelGroups[AUDIO_TYPE_AMBIENT]); + } + // set up our favourite FMOD-native streaming audio implementation if none has already been added if (!getStreamingAudioImpl()) // no existing implementation added setStreamingAudioImpl(new LLStreamingAudio_FMODEX(mSystem)); diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index 966455a46..3ff461a58 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -49,6 +49,8 @@ #include "sound_ids.h" +#include + #if LL_WINDOWS //Some ugly code to make missing fmod.dll not cause a fatal error. #define WIN32_LEAN_AND_MEAN #include "windows.h" @@ -77,7 +79,7 @@ bool attemptDelayLoad() static bool sVerboseDebugging = false; -FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); +FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *originalbuffer, float *newbuffer, unsigned int length, int inchannels, int *outchannels); FMOD::ChannelGroup *LLAudioEngine_FMODSTUDIO::mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT] = {0}; @@ -314,14 +316,10 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) result = mSystem->setSoftwareChannels(num_channels + 2); Check_FMOD_Error(result,"FMOD::System::setSoftwareChannels"); - U32 fmod_flags = FMOD_INIT_NORMAL | FMOD_INIT_3D_RIGHTHANDED; + U32 fmod_flags = FMOD_INIT_NORMAL | FMOD_INIT_3D_RIGHTHANDED | FMOD_INIT_THREAD_UNSAFE; if(mEnableProfiler) { fmod_flags |= FMOD_INIT_PROFILE_ENABLE; - mSystem->createChannelGroup("None", &mChannelGroups[AUDIO_TYPE_NONE]); - mSystem->createChannelGroup("SFX", &mChannelGroups[AUDIO_TYPE_SFX]); - mSystem->createChannelGroup("UI", &mChannelGroups[AUDIO_TYPE_UI]); - mSystem->createChannelGroup("Ambient", &mChannelGroups[AUDIO_TYPE_AMBIENT]); } #if LL_LINUX @@ -423,7 +421,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) Ok, the speaker mode selected isn't supported by this soundcard. Switch it back to stereo... */ - result = mSystem->setSoftwareFormat(44100, FMOD_SPEAKERMODE_STEREO, 2); + result = mSystem->setSoftwareFormat(44100, FMOD_SPEAKERMODE_STEREO, 0); Check_FMOD_Error(result,"Error falling back to stereo mode"); /* ... and re-init. @@ -434,6 +432,14 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) return false; #endif + if (mEnableProfiler) + { + mSystem->createChannelGroup("None", &mChannelGroups[AUDIO_TYPE_NONE]); + mSystem->createChannelGroup("SFX", &mChannelGroups[AUDIO_TYPE_SFX]); + mSystem->createChannelGroup("UI", &mChannelGroups[AUDIO_TYPE_UI]); + mSystem->createChannelGroup("Ambient", &mChannelGroups[AUDIO_TYPE_AMBIENT]); + } + // set up our favourite FMOD-native streaming audio implementation if none has already been added if (!getStreamingAudioImpl()) // no existing implementation added setStreamingAudioImpl(new LLStreamingAudio_FMODSTUDIO(mSystem)); @@ -536,46 +542,50 @@ LLAudioChannel * LLAudioEngine_FMODSTUDIO::createChannel() bool LLAudioEngine_FMODSTUDIO::initWind() { - //mNextWindUpdate = 0.0; + mNextWindUpdate = 0.0; - //if (!mWindDSP) - //{ - // FMOD_DSP_DESCRIPTION dspdesc; - // memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION)); //Set everything to zero - // strncpy(dspdesc.name,"Wind Unit", sizeof(dspdesc.name)); //Set name to "Wind Unit" - // dspdesc.read = &windCallback; //Assign callback. - // if(Check_FMOD_Error(mSystem->createDSP(&dspdesc, &mWindDSP), "FMOD::createDSP")) - // return false; + if (!mWindDSP) + { + FMOD_DSP_DESCRIPTION dspdesc; + memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION)); //Set everything to zero + dspdesc.pluginsdkversion = FMOD_PLUGIN_SDK_VERSION; + strncpy(dspdesc.name,"Wind Unit", sizeof(dspdesc.name)); //Set name to "Wind Unit" + dspdesc.numoutputbuffers = 1; + dspdesc.read = &windCallback; //Assign callback. + if(Check_FMOD_Error(mSystem->createDSP(&dspdesc, &mWindDSP), "FMOD::createDSP")) + return false; - // if(mWindGen) - // delete mWindGen; - // - // int frequency = 44100; - // mSystem->getSoftwareFormat(&frequency, NULL, NULL); - // mWindGen = new LLWindGen((U32)frequency); - // mWindDSP->setUserData((void*)mWindGen); - //} + if(mWindGen) + delete mWindGen; + + int frequency = 44100; + mSystem->getSoftwareFormat(&frequency, NULL, NULL); + mWindGen = new LLWindGen((U32)frequency); + mWindDSP->setUserData((void*)mWindGen); + } - //if (mWindDSP) - //{ - // mSystem->playDSP(mWindDSP, NULL, false, 0); - // return true; - //} + if (mWindDSP) + { + mSystem->playDSP(mWindDSP, NULL, false, 0); + FMOD_SPEAKERMODE mode; + mSystem->getSoftwareFormat(NULL, &mode, NULL); + mWindDSP->setChannelFormat(FMOD_CHANNELMASK_STEREO, 2, mode); + return true; + } return false; } void LLAudioEngine_FMODSTUDIO::cleanupWind() { - //if (mWindDSP) - //{ - // mWindDSP->remove(); - // mWindDSP->release(); - // mWindDSP = NULL; - //} + if (mWindDSP) + { + mWindDSP->release(); + mWindDSP = NULL; + } - //delete mWindGen; - //mWindGen = NULL; + delete mWindGen; + mWindGen = NULL; } diff --git a/indra/llaudio/llstreamingaudio_fmodex.cpp b/indra/llaudio/llstreamingaudio_fmodex.cpp index be5888aa5..2cfae5128 100644 --- a/indra/llaudio/llstreamingaudio_fmodex.cpp +++ b/indra/llaudio/llstreamingaudio_fmodex.cpp @@ -289,6 +289,8 @@ void LLStreamingAudio_FMODEX::update() case(FMOD_TAGDATATYPE_STRING): { std::string out = rawstr_to_utf8(std::string((char*)tag.data,tag.datalen)); + if (out.length() && out.back() == 0) + out.pop_back(); (*mMetaData)[name]=out; llinfos << tag.name << "(RAW): " << out << llendl; } @@ -299,6 +301,8 @@ void LLStreamingAudio_FMODEX::update() if (tag.datalen > 3 && ((char*)tag.data)[0] == 0xEF && ((char*)tag.data)[1] == 0xBB && ((char*)tag.data)[2] == 0xBF) offs = 3; std::string out((char*)tag.data + offs, tag.datalen - offs); + if (out.length() && out.back() == 0) + out.pop_back(); (*mMetaData)[name] = out; llinfos << tag.name << "(UTF8): " << out << llendl; } @@ -306,6 +310,8 @@ void LLStreamingAudio_FMODEX::update() case(FMOD_TAGDATATYPE_STRING_UTF16): { std::string out = utf16input_to_utf8((char*)tag.data, tag.datalen, UTF16); + if (out.length() && out.back() == 0) + out.pop_back(); (*mMetaData)[name] = out; llinfos << tag.name << "(UTF16): " << out << llendl; } @@ -313,6 +319,8 @@ void LLStreamingAudio_FMODEX::update() case(FMOD_TAGDATATYPE_STRING_UTF16BE): { std::string out = utf16input_to_utf8((char*)tag.data, tag.datalen, UTF16BE); + if (out.length() && out.back() == 0) + out.pop_back(); (*mMetaData)[name] = out; llinfos << tag.name << "(UTF16BE): " << out << llendl; } diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.cpp b/indra/llaudio/llstreamingaudio_fmodstudio.cpp index a2ffe2108..0c7b3d2c4 100644 --- a/indra/llaudio/llstreamingaudio_fmodstudio.cpp +++ b/indra/llaudio/llstreamingaudio_fmodstudio.cpp @@ -33,17 +33,25 @@ #include "linden_common.h" #include "llmath.h" +#include "llthread.h" #include "fmod.hpp" #include "fmod_errors.h" #include "llstreamingaudio_fmodstudio.h" +inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string) +{ + if (result == FMOD_OK) + return false; + LL_WARNS("AudioImpl") << string << " Error: " << FMOD_ErrorString(result) << LL_ENDL; + return true; +} class LLAudioStreamManagerFMODSTUDIO { public: - LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, const std::string& url); + LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, FMOD::ChannelGroup *group, const std::string& url); FMOD::Channel* startStream(); bool stopStream(); // Returns true if the stream was successfully stopped. bool ready(); @@ -55,12 +63,55 @@ protected: FMOD::System* mSystem; FMOD::Channel* mStreamChannel; FMOD::Sound* mInternetStream; + FMOD::ChannelGroup* mChannelGroup; bool mReady; std::string mInternetStreamURL; }; +LLGlobalMutex gWaveDataMutex; //Just to be extra strict. +const U32 WAVE_BUFFER_SIZE = 1024; +U32 gWaveBufferMinSize = 0; +F32 gWaveDataBuffer[WAVE_BUFFER_SIZE] = { 0.f }; +U32 gWaveDataBufferSize = 0; +FMOD_RESULT F_CALLBACK waveDataCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels) +{ + if (!length || !inchannels) + return FMOD_OK; + memcpy(outbuffer, inbuffer, length * inchannels * sizeof(float)); + + static std::vector local_buf; + if (local_buf.size() < length) + local_buf.resize(length, 0.f); + + for (U32 i = 0; i < length; ++i) + { + F32 total = 0.f; + for (S32 j = 0; j < inchannels; ++j) + { + total += inbuffer[i*inchannels + j]; + } + local_buf[i] = total / inchannels; + } + + { + LLMutexLock lock(gWaveDataMutex); + + for (U32 i = length; i > 0; --i) + { + if (++gWaveDataBufferSize > WAVE_BUFFER_SIZE) + { + if (gWaveBufferMinSize) + memcpy(gWaveDataBuffer + WAVE_BUFFER_SIZE - gWaveBufferMinSize, gWaveDataBuffer, gWaveBufferMinSize * sizeof(float)); + gWaveDataBufferSize = 1 + gWaveBufferMinSize; + } + gWaveDataBuffer[WAVE_BUFFER_SIZE - gWaveDataBufferSize] = local_buf[i - 1]; + } + } + + return FMOD_OK; +} //--------------------------------------------------------------------------- // Internet Streaming @@ -72,11 +123,14 @@ LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) : mGain(1.0f), mMetaData(NULL) { + FMOD_RESULT result; + // Number of milliseconds of audio to buffer for the audio card. // Must be larger than the usual Second Life frame stutter time. const U32 buffer_seconds = 10; //sec const U32 estimated_bitrate = 128; //kbit/sec - mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES); + result = mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES); + Check_FMOD_Error(result, "FMOD::System::setStreamBufferSize"); // Here's where we set the size of the network buffer and some buffering // parameters. In this case we want a network buffer of 16k, we want it @@ -85,11 +139,36 @@ LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) : // Leave the net buffer properties at the default. //FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80); + + result = system->createChannelGroup("stream", &mStreamGroup); + Check_FMOD_Error(result,"FMOD::System::createChannelGroup"); + + FMOD_DSP_DESCRIPTION dspdesc; + memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION)); //Zero out everything + dspdesc.pluginsdkversion = FMOD_PLUGIN_SDK_VERSION; + strncpy(dspdesc.name, "Waveform", sizeof(dspdesc.name)); + dspdesc.numoutputbuffers = 1; + dspdesc.read = &waveDataCallback; //Assign callback. + + result = system->createDSP(&dspdesc, &mStreamDSP); + Check_FMOD_Error(result, "FMOD::System::createDSPByType"); + result = mStreamGroup->addDSP(FMOD_CHANNELCONTROL_DSP_TAIL, mStreamDSP); + Check_FMOD_Error(result, "FMOD::ChannelGroup::addDSP"); + mStreamDSP->setActive(false); } LLStreamingAudio_FMODSTUDIO::~LLStreamingAudio_FMODSTUDIO() { + + if (mStreamGroup) + { + if (mStreamDSP) + mStreamGroup->removeDSP(mStreamDSP); + mStreamGroup->release(); + } + if (mStreamDSP) + mStreamDSP->release(); // nothing interesting/safe to do. } @@ -110,7 +189,7 @@ void LLStreamingAudio_FMODSTUDIO::start(const std::string& url) if(mDeadStreams.empty()) { llinfos << "Starting internet stream: " << url << llendl; - mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem,url); + mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem, mStreamGroup, url); mURL = url; mMetaData = new LLSD; } @@ -127,6 +206,45 @@ void LLStreamingAudio_FMODSTUDIO::start(const std::string& url) } } +enum utf_endian_type_t +{ + UTF16LE, + UTF16BE, + UTF16 +}; + +std::string utf16input_to_utf8(char* input, U32 len, utf_endian_type_t type) +{ + if (type == UTF16) + { + type = UTF16BE; //Default + if (len > 2) + { + //Parse and strip BOM. + if ((input[0] == 0xFE && input[1] == 0xFF) || + (input[0] == 0xFF && input[1] == 0xFE)) + { + input += 2; + len -= 2; + type = input[0] == 0xFE ? UTF16BE : UTF16LE; + } + } + } + llutf16string out_16((U16*)input, len / 2); + if (len % 2) + { + out_16.push_back((input)[len - 1] << 8); + } + if (type == UTF16BE) + { + for (llutf16string::iterator i = out_16.begin(); i < out_16.end(); ++i) + { + llutf16string::value_type v = *i; + *i = ((v & 0x00FF) << 8) | ((v & 0xFF00) >> 8); + } + } + return utf16str_to_utf8str(out_16); +} void LLStreamingAudio_FMODSTUDIO::update() { @@ -157,7 +275,7 @@ void LLStreamingAudio_FMODSTUDIO::update() { llassert_always(mCurrentInternetStreamp == NULL); llinfos << "Starting internet stream: " << mPendingURL << llendl; - mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem,mPendingURL); + mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem,mStreamGroup, mPendingURL); mURL = mPendingURL; mMetaData = new LLSD; mPendingURL.clear(); @@ -184,6 +302,8 @@ void LLStreamingAudio_FMODSTUDIO::update() { // Reset volume to previously set volume setGain(getGain()); + if (mStreamDSP) + mStreamDSP->setActive(true); mFMODInternetStreamChannelp->setPaused(false); } } @@ -216,23 +336,27 @@ void LLStreamingAudio_FMODSTUDIO::update() switch(tag.type) //Crappy tag translate table. { case(FMOD_TAGTYPE_ID3V2): - if(name == "TIT2") name = "TITLE"; + if (!LLStringUtil::compareInsensitive(name, "TIT2")) name = "TITLE"; else if(name == "TPE1") name = "ARTIST"; break; case(FMOD_TAGTYPE_ASF): - if(name == "Title") name = "TITLE"; - else if(name == "WM/AlbumArtist") name = "ARTIST"; + if (!LLStringUtil::compareInsensitive(name, "Title")) name = "TITLE"; + else if (!LLStringUtil::compareInsensitive(name, "WM/AlbumArtist")) name = "ARTIST"; break; case(FMOD_TAGTYPE_FMOD): - if (!strcmp(tag.name, "Sample Rate Change")) + if (!LLStringUtil::compareInsensitive(name, "Sample Rate Change")) { llinfos << "Stream forced changing sample rate to " << *((float *)tag.data) << llendl; mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data)); } continue; default: + if (!LLStringUtil::compareInsensitive(name, "TITLE") || + !LLStringUtil::compareInsensitive(name, "ARTIST")) + LLStringUtil::toUpper(name); break; } + switch(tag.datatype) { case(FMOD_TAGDATATYPE_INT): @@ -246,25 +370,40 @@ void LLStreamingAudio_FMODSTUDIO::update() case(FMOD_TAGDATATYPE_STRING): { std::string out = rawstr_to_utf8(std::string((char*)tag.data,tag.datalen)); + if (out.length() && out.back() == 0) + out.pop_back(); (*mMetaData)[name]=out; - llinfos << tag.name << ": " << out << llendl; + llinfos << tag.name << "(RAW): " << out << llendl; + } + break; + case(FMOD_TAGDATATYPE_STRING_UTF8) : + { + U8 offs = 0; + if (tag.datalen > 3 && ((char*)tag.data)[0] == 0xEF && ((char*)tag.data)[1] == 0xBB && ((char*)tag.data)[2] == 0xBF) + offs = 3; + std::string out((char*)tag.data + offs, tag.datalen - offs); + if (out.length() && out.back() == 0) + out.pop_back(); + (*mMetaData)[name] = out; + llinfos << tag.name << "(UTF8): " << out << llendl; } break; case(FMOD_TAGDATATYPE_STRING_UTF16): { - std::string out((char*)tag.data,tag.datalen); - (*mMetaData)[std::string(tag.name)]=out; - llinfos << tag.name << ": " << out << llendl; + std::string out = utf16input_to_utf8((char*)tag.data, tag.datalen, UTF16); + if (out.length() && out.back() == 0) + out.pop_back(); + (*mMetaData)[name] = out; + llinfos << tag.name << "(UTF16): " << out << llendl; } break; case(FMOD_TAGDATATYPE_STRING_UTF16BE): { - std::string out((char*)tag.data,tag.datalen); - U16* buf = (U16*)out.c_str(); - for(U32 j = 0; j < out.size()/2; ++j) - (((buf[j] & 0xff)<<8) | ((buf[j] & 0xff00)>>8)); - (*mMetaData)[std::string(tag.name)]=out; - llinfos << tag.name << ": " << out << llendl; + std::string out = utf16input_to_utf8((char*)tag.data, tag.datalen, UTF16BE); + if (out.length() && out.back() == 0) + out.pop_back(); + (*mMetaData)[name] = out; + llinfos << tag.name << "(UTF16BE): " << out << llendl; } default: break; @@ -300,6 +439,15 @@ void LLStreamingAudio_FMODSTUDIO::stop() delete mMetaData; mMetaData = NULL; } + + if (mStreamDSP) + { + mSystem->lockDSP(); + mStreamDSP->setActive(false); + gWaveDataBufferSize = 0; + mSystem->unlockDSP(); + } + if (mFMODInternetStreamChannelp) { mFMODInternetStreamChannelp->setPaused(true); @@ -390,38 +538,40 @@ void LLStreamingAudio_FMODSTUDIO::setGain(F32 vol) /*virtual*/ bool LLStreamingAudio_FMODSTUDIO::getWaveData(float* arr, S32 count, S32 stride/*=1*/) { - //if(!mFMODInternetStreamChannelp || !mCurrentInternetStreamp) - // return false; + if (count > (WAVE_BUFFER_SIZE / 2)) + LL_ERRS("AudioImpl") << "Count=" << count << " exceeds WAVE_BUFFER_SIZE/2=" << WAVE_BUFFER_SIZE << LL_ENDL; - //bool muted=false; - //mFMODInternetStreamChannelp->getMute(&muted); - //if(muted) - // return false; + if(!mFMODInternetStreamChannelp || !mCurrentInternetStreamp) + return false; - //static std::vector local_array(count); //Have to have an extra buffer to mix channels. Bleh. - //if(count > (S32)local_array.size()) //Expand the array if needed. Try to minimize allocation calls, so don't ever shrink. - // local_array.resize(count); - - //if( mFMODInternetStreamChannelp->getWaveData(&local_array[0],count,0) == FMOD_OK && - // mFMODInternetStreamChannelp->getWaveData(&arr[0],count,1) == FMOD_OK ) - //{ - // for(S32 i = count-1;i>=0;i-=stride) - // { - // arr[i] += local_array[i]; - // arr[i] *= .5f; - // } - // return true; - //} - return false; + bool muted = false; + mFMODInternetStreamChannelp->getMute(&muted); + if(muted) + return false; + { + U32 buff_size; + { + LLMutexLock lock(gWaveDataMutex); + gWaveBufferMinSize = count; + buff_size = gWaveDataBufferSize; + if (!buff_size) + return false; + memcpy(arr, gWaveDataBuffer + WAVE_BUFFER_SIZE - buff_size, llmin(U32(count), buff_size) * sizeof(float)); + } + if (buff_size < U32(count)) + memset(arr + buff_size, 0, (count - buff_size) * sizeof(float)); + } + return true; } /////////////////////////////////////////////////////// // manager of possibly-multiple internet audio streams -LLAudioStreamManagerFMODSTUDIO::LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, const std::string& url) : +LLAudioStreamManagerFMODSTUDIO::LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, FMOD::ChannelGroup *group, const std::string& url) : mSystem(system), mStreamChannel(NULL), mInternetStream(NULL), + mChannelGroup(group), mReady(false) { mInternetStreamURL = url; @@ -452,7 +602,7 @@ FMOD::Channel *LLAudioStreamManagerFMODSTUDIO::startStream() if(mStreamChannel) return mStreamChannel; //Already have a channel for this stream. - mSystem->playSound(mInternetStream, NULL, true, &mStreamChannel); + mSystem->playSound(mInternetStream, mChannelGroup, true, &mStreamChannel); return mStreamChannel; } diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.h b/indra/llaudio/llstreamingaudio_fmodstudio.h index 142256339..a71a88eba 100644 --- a/indra/llaudio/llstreamingaudio_fmodstudio.h +++ b/indra/llaudio/llstreamingaudio_fmodstudio.h @@ -45,6 +45,9 @@ namespace FMOD { class System; class Channel; + class ChannelGroup; + class ChannelGroup; + class DSP; } //Interfaces @@ -65,10 +68,11 @@ class LLStreamingAudio_FMODSTUDIO : public LLStreamingAudioInterface /*virtual*/ bool supportsMetaData(){return true;} /*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not playing. - /*virtual*/ bool supportsWaveData(){return false;} + /*virtual*/ bool supportsWaveData(){return true;} /*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1); /*virtual*/ bool supportsAdjustableBufferSizes(){return true;} /*virtual*/ void setBufferSizes(U32 streambuffertime, U32 decodebuffertime); + private: FMOD::System *mSystem; @@ -81,6 +85,9 @@ private: F32 mGain; LLSD *mMetaData; + + FMOD::ChannelGroup* mStreamGroup; + FMOD::DSP* mStreamDSP; }; From 87f87bf2ffbddd738950ee4d7b3ed140efa86e6f Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 25 Nov 2014 16:37:39 -0600 Subject: [PATCH 16/55] Delegate stream shutdown to LLAudioEngine and LLStreamingAudio_* --- indra/llaudio/llaudioengine.cpp | 3 ++ indra/llaudio/llaudioengine_fmodex.cpp | 2 - indra/llaudio/llaudioengine_fmodstudio.cpp | 2 - indra/llaudio/llstreamingaudio_fmodex.cpp | 49 ++++++++++++------- indra/llaudio/llstreamingaudio_fmodex.h | 2 + indra/llaudio/llstreamingaudio_fmodstudio.cpp | 49 ++++++++++++------- indra/llaudio/llstreamingaudio_fmodstudio.h | 2 + indra/newview/llappviewer.cpp | 6 --- 8 files changed, 67 insertions(+), 48 deletions(-) diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index 6be8a8042..99e151f12 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -174,6 +174,9 @@ void LLAudioEngine::shutdown() delete mBuffers[i]; mBuffers[i] = NULL; } + + delete mStreamingAudioImpl; + mStreamingAudioImpl = NULL; } diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index c3e067c59..6417a901d 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -513,8 +513,6 @@ void LLAudioEngine_FMODEX::allocateListener(void) void LLAudioEngine_FMODEX::shutdown() { - stopInternetStream(); - LL_INFOS("AudioImpl") << "About to LLAudioEngine::shutdown()" << LL_ENDL; LLAudioEngine::shutdown(); diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index 3ff461a58..09bc4531d 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -509,8 +509,6 @@ void LLAudioEngine_FMODSTUDIO::allocateListener(void) void LLAudioEngine_FMODSTUDIO::shutdown() { - stopInternetStream(); - LL_INFOS("AudioImpl") << "About to LLAudioEngine::shutdown()" << LL_ENDL; LLAudioEngine::shutdown(); diff --git a/indra/llaudio/llstreamingaudio_fmodex.cpp b/indra/llaudio/llstreamingaudio_fmodex.cpp index 2cfae5128..154b1fc34 100644 --- a/indra/llaudio/llstreamingaudio_fmodex.cpp +++ b/indra/llaudio/llstreamingaudio_fmodex.cpp @@ -90,7 +90,13 @@ LLStreamingAudio_FMODEX::LLStreamingAudio_FMODEX(FMOD::System *system) : LLStreamingAudio_FMODEX::~LLStreamingAudio_FMODEX() { - // nothing interesting/safe to do. + stop(); + for (U32 i = 0; i < 100; ++i) + { + if (releaseDeadStreams()) + break; + ms_sleep(10); + } } @@ -169,24 +175,7 @@ std::string utf16input_to_utf8(char* input, U32 len, utf_endian_type_t type) void LLStreamingAudio_FMODEX::update() { - // Kill dead internet streams, if possible - std::list::iterator iter; - for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();) - { - LLAudioStreamManagerFMODEX *streamp = *iter; - if (streamp->stopStream()) - { - llinfos << "Closed dead stream" << llendl; - delete streamp; - mDeadStreams.erase(iter++); - } - else - { - iter++; - } - } - - if(!mDeadStreams.empty()) + if (!releaseDeadStreams()) { llassert_always(mCurrentInternetStreamp == NULL); return; @@ -570,3 +559,25 @@ void LLStreamingAudio_FMODEX::setBufferSizes(U32 streambuffertime, U32 decodebuf settings.defaultDecodeBufferSize = decodebuffertime;//ms mSystem->setAdvancedSettings(&settings); } + +bool LLStreamingAudio_FMODEX::releaseDeadStreams() +{ + // Kill dead internet streams, if possible + std::list::iterator iter; + for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();) + { + LLAudioStreamManagerFMODSTUDIO *streamp = *iter; + if (streamp->stopStream()) + { + llinfos << "Closed dead stream" << llendl; + delete streamp; + mDeadStreams.erase(iter++); + } + else + { + iter++; + } + } + + return mDeadStreams.empty(); +} \ No newline at end of file diff --git a/indra/llaudio/llstreamingaudio_fmodex.h b/indra/llaudio/llstreamingaudio_fmodex.h index 8e8bb2da6..15a4af931 100644 --- a/indra/llaudio/llstreamingaudio_fmodex.h +++ b/indra/llaudio/llstreamingaudio_fmodex.h @@ -70,6 +70,8 @@ class LLStreamingAudio_FMODEX : public LLStreamingAudioInterface /*virtual*/ bool supportsAdjustableBufferSizes(){return true;} /*virtual*/ void setBufferSizes(U32 streambuffertime, U32 decodebuffertime); private: + bool releaseDeadStreams(); + FMOD::System *mSystem; LLAudioStreamManagerFMODEX *mCurrentInternetStreamp; diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.cpp b/indra/llaudio/llstreamingaudio_fmodstudio.cpp index 0c7b3d2c4..cfe8c05c3 100644 --- a/indra/llaudio/llstreamingaudio_fmodstudio.cpp +++ b/indra/llaudio/llstreamingaudio_fmodstudio.cpp @@ -160,6 +160,13 @@ LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) : LLStreamingAudio_FMODSTUDIO::~LLStreamingAudio_FMODSTUDIO() { + stop(); + for (U32 i = 0; i < 100; ++i) + { + if (releaseDeadStreams()) + break; + ms_sleep(10); + } if (mStreamGroup) { @@ -169,7 +176,6 @@ LLStreamingAudio_FMODSTUDIO::~LLStreamingAudio_FMODSTUDIO() } if (mStreamDSP) mStreamDSP->release(); - // nothing interesting/safe to do. } @@ -248,24 +254,7 @@ std::string utf16input_to_utf8(char* input, U32 len, utf_endian_type_t type) void LLStreamingAudio_FMODSTUDIO::update() { - // Kill dead internet streams, if possible - std::list::iterator iter; - for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();) - { - LLAudioStreamManagerFMODSTUDIO *streamp = *iter; - if (streamp->stopStream()) - { - llinfos << "Closed dead stream" << llendl; - delete streamp; - mDeadStreams.erase(iter++); - } - else - { - iter++; - } - } - - if(!mDeadStreams.empty()) + if (!releaseDeadStreams()) { llassert_always(mCurrentInternetStreamp == NULL); return; @@ -653,3 +642,25 @@ void LLStreamingAudio_FMODSTUDIO::setBufferSizes(U32 streambuffertime, U32 decod settings.defaultDecodeBufferSize = decodebuffertime;//ms mSystem->setAdvancedSettings(&settings); } + +bool LLStreamingAudio_FMODSTUDIO::releaseDeadStreams() +{ + // Kill dead internet streams, if possible + std::list::iterator iter; + for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();) + { + LLAudioStreamManagerFMODSTUDIO *streamp = *iter; + if (streamp->stopStream()) + { + llinfos << "Closed dead stream" << llendl; + delete streamp; + mDeadStreams.erase(iter++); + } + else + { + iter++; + } + } + + return mDeadStreams.empty(); +} \ No newline at end of file diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.h b/indra/llaudio/llstreamingaudio_fmodstudio.h index a71a88eba..831e2407e 100644 --- a/indra/llaudio/llstreamingaudio_fmodstudio.h +++ b/indra/llaudio/llstreamingaudio_fmodstudio.h @@ -74,6 +74,8 @@ class LLStreamingAudio_FMODSTUDIO : public LLStreamingAudioInterface /*virtual*/ void setBufferSizes(U32 streambuffertime, U32 decodebuffertime); private: + bool releaseDeadStreams(); + FMOD::System *mSystem; LLAudioStreamManagerFMODSTUDIO *mCurrentInternetStreamp; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index e35d5ab27..3ed202986 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -4479,12 +4479,6 @@ void LLAppViewer::shutdownAudio() { if (gAudiop) { - // shut down the streaming audio sub-subsystem first, in case it relies on not outliving the general audio subsystem. - - LLStreamingAudioInterface *sai = gAudiop->getStreamingAudioImpl(); - delete sai; - gAudiop->setStreamingAudioImpl(NULL); - // shut down the audio subsystem bool want_longname = false; From f5204cc8f5fbacb39edce5deb7dbb61a44df0f12 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Wed, 26 Nov 2014 01:15:17 -0600 Subject: [PATCH 17/55] Check for bad FMOD_RESULT return values for practically every fmod api call. Wavedata dsp also now attached before fmod's fader DSP, and although that makes the stream channelgroup less than necessary, channelgroups are still nice to have. --- indra/llaudio/llaudioengine_fmodex.cpp | 155 ++++++++-------- indra/llaudio/llaudioengine_fmodstudio.cpp | 170 +++++++++--------- indra/llaudio/llstreamingaudio_fmodex.cpp | 91 ++++++---- indra/llaudio/llstreamingaudio_fmodstudio.cpp | 125 +++++++------ indra/llaudio/llstreamingaudio_fmodstudio.h | 1 + 5 files changed, 287 insertions(+), 255 deletions(-) diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index 6417a901d..25d5754fb 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -285,7 +285,7 @@ bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) return false; #endif - U32 version; + U32 version = 0; FMOD_RESULT result; LL_DEBUGS("AppInit") << "LLAudioEngine_FMODEX::init() initializing FMOD" << LL_ENDL; @@ -331,7 +331,7 @@ bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) if (NULL == getenv("LL_BAD_FMOD_PULSEAUDIO")) /*Flawfinder: ignore*/ { LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL; - if(mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO) == FMOD_OK && + if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO)) == FMOD_OK && (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) { LL_DEBUGS("AppInit") << "PulseAudio output initialized OKAY" << LL_ENDL; @@ -352,7 +352,7 @@ bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) if (NULL == getenv("LL_BAD_FMOD_ALSA")) /*Flawfinder: ignore*/ { LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL; - if(mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA) == FMOD_OK && + if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA)) == FMOD_OK && (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) { LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL; @@ -373,7 +373,7 @@ bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) if (NULL == getenv("LL_BAD_FMOD_OSS")) /*Flawfinder: ignore*/ { LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL; - if(mSystem->setOutput(FMOD_OUTPUTTYPE_OSS) == FMOD_OK && + if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_OSS) == FMOD_OK) && (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) { LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL; @@ -398,20 +398,22 @@ bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) // We're interested in logging which output method we // ended up with, for QA purposes. FMOD_OUTPUTTYPE output_type; - mSystem->getOutput(&output_type); - switch (output_type) + if(!Check_FMOD_Error(mSystem->getOutput(&output_type), "FMOD::System::getOutput")) { - case FMOD_OUTPUTTYPE_NOSOUND: - LL_INFOS("AppInit") << "Audio output: NoSound" << LL_ENDL; break; - case FMOD_OUTPUTTYPE_PULSEAUDIO: - LL_INFOS("AppInit") << "Audio output: PulseAudio" << LL_ENDL; break; - case FMOD_OUTPUTTYPE_ALSA: - LL_INFOS("AppInit") << "Audio output: ALSA" << LL_ENDL; break; - case FMOD_OUTPUTTYPE_OSS: - LL_INFOS("AppInit") << "Audio output: OSS" << LL_ENDL; break; - default: - LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break; - }; + switch (output_type) + { + case FMOD_OUTPUTTYPE_NOSOUND: + LL_INFOS("AppInit") << "Audio output: NoSound" << LL_ENDL; break; + case FMOD_OUTPUTTYPE_PULSEAUDIO: + LL_INFOS("AppInit") << "Audio output: PulseAudio" << LL_ENDL; break; + case FMOD_OUTPUTTYPE_ALSA: + LL_INFOS("AppInit") << "Audio output: ALSA" << LL_ENDL; break; + case FMOD_OUTPUTTYPE_OSS: + LL_INFOS("AppInit") << "Audio output: OSS" << LL_ENDL; break; + default: + LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break; + }; + } #else // LL_LINUX // initialize the FMOD engine @@ -435,10 +437,10 @@ bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) if (mEnableProfiler) { - mSystem->createChannelGroup("None", &mChannelGroups[AUDIO_TYPE_NONE]); - mSystem->createChannelGroup("SFX", &mChannelGroups[AUDIO_TYPE_SFX]); - mSystem->createChannelGroup("UI", &mChannelGroups[AUDIO_TYPE_UI]); - mSystem->createChannelGroup("Ambient", &mChannelGroups[AUDIO_TYPE_AMBIENT]); + Check_FMOD_Error(mSystem->createChannelGroup("None", &mChannelGroups[AUDIO_TYPE_NONE]), "FMOD::System::createChannelGroup"); + Check_FMOD_Error(mSystem->createChannelGroup("SFX", &mChannelGroups[AUDIO_TYPE_SFX]), "FMOD::System::createChannelGroup"); + Check_FMOD_Error(mSystem->createChannelGroup("UI", &mChannelGroups[AUDIO_TYPE_UI]), "FMOD::System::createChannelGroup"); + Check_FMOD_Error(mSystem->createChannelGroup("Ambient", &mChannelGroups[AUDIO_TYPE_AMBIENT]), "FMOD::System::createChannelGroup"); } // set up our favourite FMOD-native streaming audio implementation if none has already been added @@ -451,35 +453,40 @@ bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) unsigned int r_bufferlength; char r_name[256]; FMOD_SPEAKERMODE speaker_mode; - mSystem->getDSPBufferSize(&r_bufferlength, &r_numbuffers); - mSystem->getSoftwareFormat(&r_samplerate, NULL, &r_channels, NULL, NULL, &r_bits); - mSystem->getDriverInfo(0, r_name, 255, 0); - mSystem->getSpeakerMode(&speaker_mode); - std::string speaker_mode_str = "unknown"; - switch(speaker_mode) + if (!Check_FMOD_Error(mSystem->getDSPBufferSize(&r_bufferlength, &r_numbuffers), "FMOD::System::getDSPBufferSize") && + !Check_FMOD_Error(mSystem->getSoftwareFormat(&r_samplerate, NULL, &r_channels, NULL, NULL, &r_bits), "FMOD::System::getSoftwareFormat") && + !Check_FMOD_Error(mSystem->getDriverInfo(0, r_name, 255, 0), "FMOD::System::getDriverInfo") && + !Check_FMOD_Error(mSystem->getSpeakerMode(&speaker_mode), "FMOD::System::getSpeakerMode")) { - #define SPEAKER_MODE_CASE(MODE) case MODE: speaker_mode_str = #MODE; break; - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_RAW) - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_MONO) - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_STEREO) - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_QUAD) - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_SURROUND) - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_5POINT1) - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_7POINT1) - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_SRS5_1_MATRIX) - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_MYEARS) - default:; - #undef SPEAKER_MODE_CASE + std::string speaker_mode_str = "unknown"; + switch(speaker_mode) + { + #define SPEAKER_MODE_CASE(MODE) case MODE: speaker_mode_str = #MODE; break; + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_RAW) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_MONO) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_STEREO) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_QUAD) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_SURROUND) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_5POINT1) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_7POINT1) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_SRS5_1_MATRIX) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_MYEARS) + default:; + #undef SPEAKER_MODE_CASE + } + + r_name[255] = '\0'; + int latency = 1000.0 * r_bufferlength * r_numbuffers /r_samplerate; + + LL_INFOS("AppInit") << "FMOD device: "<< r_name << "\n" + << "Output mode: "<< speaker_mode_str << "\n" + << "FMOD Ex parameters: " << r_samplerate << " Hz * " << r_channels << " * " <close(); + Check_FMOD_Error(mSystem->close(), "FMOD::System::close"); LL_INFOS("AudioImpl") << "LLAudioEngine_FMODEX::shutdown() Requesting FMOD Ex system release" << LL_ENDL; - mSystem->release(); + Check_FMOD_Error(mSystem->release(), "FMOD::System::release"); } LL_INFOS("AudioImpl") << "LLAudioEngine_FMODEX::shutdown() done closing FMOD Ex" << LL_ENDL; @@ -546,30 +553,27 @@ bool LLAudioEngine_FMODEX::initWind() { mNextWindUpdate = 0.0; - if (!mWindDSP) - { - FMOD_DSP_DESCRIPTION dspdesc; - memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION)); //Set everything to zero - strncpy(dspdesc.name,"Wind Unit", sizeof(dspdesc.name)); //Set name to "Wind Unit" - dspdesc.channels=2; - dspdesc.read = &windCallback; //Assign callback. - if(Check_FMOD_Error(mSystem->createDSP(&dspdesc, &mWindDSP), "FMOD::createDSP")) - return false; + cleanupWind(); - if(mWindGen) - delete mWindGen; + + FMOD_DSP_DESCRIPTION dspdesc; + memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION)); //Set everything to zero + strncpy(dspdesc.name,"Wind Unit", sizeof(dspdesc.name)); //Set name to "Wind Unit" + dspdesc.channels=2; + dspdesc.read = &windCallback; //Assign callback. + if(Check_FMOD_Error(mSystem->createDSP(&dspdesc, &mWindDSP), "FMOD::createDSP") || !mWindDSP) + return false; - float frequency = 44100; - mWindDSP->getDefaults(&frequency,0,0,0); + float frequency = 44100; + if (!Check_FMOD_Error(mWindDSP->getDefaults(&frequency,0,0,0), "FMOD::DSP::getDefaults")) + { mWindGen = new LLWindGen((U32)frequency); - mWindDSP->setUserData((void*)mWindGen); + if (!Check_FMOD_Error(mWindDSP->setUserData((void*)mWindGen), "FMOD::DSP::setUserData") && + !Check_FMOD_Error(mSystem->playDSP(FMOD_CHANNEL_FREE, mWindDSP, false, 0), "FMOD::System::playDSP")) + return true; //Success } - if (mWindDSP) - { - mSystem->playDSP(FMOD_CHANNEL_FREE, mWindDSP, false, 0); - return true; - } + cleanupWind(); return false; } @@ -578,8 +582,8 @@ void LLAudioEngine_FMODEX::cleanupWind() { if (mWindDSP) { - mWindDSP->remove(); - mWindDSP->release(); + Check_FMOD_Error(mWindDSP->remove(), "FMOD::DSP::remove"); + Check_FMOD_Error(mWindDSP->release(), "FMOD::DSP::release"); mWindDSP = NULL; } @@ -631,8 +635,8 @@ void LLAudioEngine_FMODEX::setInternalGain(F32 gain) gain = llclamp( gain, 0.0f, 1.0f ); FMOD::ChannelGroup *master_group; - mSystem->getMasterChannelGroup(&master_group); - + if(Check_FMOD_Error(mSystem->getMasterChannelGroup(&master_group), "FMOD::System::getMasterChannelGroup")) + return; master_group->setVolume(gain); LLStreamingAudioInterface *saimpl = getStreamingAudioImpl(); @@ -1015,10 +1019,9 @@ FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *originalbu FMOD::DSP *thisdsp = (FMOD::DSP *)dsp_state->instance; thisdsp->getUserData((void **)&windgen); - S32 channels, configwidth, configheight; - thisdsp->getInfo(0, 0, &channels, &configwidth, &configheight); - windgen->windGenerate((LLAudioEngine_FMODEX::MIXBUFFERFORMAT *)newbuffer, length); + if (windgen) + windgen->windGenerate((LLAudioEngine_FMODEX::MIXBUFFERFORMAT *)newbuffer, length); return FMOD_OK; } diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index 09bc4531d..4da18d25e 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -79,7 +79,7 @@ bool attemptDelayLoad() static bool sVerboseDebugging = false; -FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *originalbuffer, float *newbuffer, unsigned int length, int inchannels, int *outchannels); +FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels); FMOD::ChannelGroup *LLAudioEngine_FMODSTUDIO::mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT] = {0}; @@ -287,7 +287,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) return false; #endif - U32 version; + U32 version = 0; FMOD_RESULT result; LL_DEBUGS("AppInit") << "LLAudioEngine_FMODSTUDIO::init() initializing FMOD" << LL_ENDL; @@ -330,7 +330,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) if (NULL == getenv("LL_BAD_FMOD_PULSEAUDIO")) /*Flawfinder: ignore*/ { LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL; - if(mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO) == FMOD_OK && + if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO)) == FMOD_OK && (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) { LL_DEBUGS("AppInit") << "PulseAudio output initialized OKAY" << LL_ENDL; @@ -351,7 +351,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) if (NULL == getenv("LL_BAD_FMOD_ALSA")) /*Flawfinder: ignore*/ { LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL; - if(mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA) == FMOD_OK && + if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA)) == FMOD_OK && (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) { LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL; @@ -372,7 +372,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) if (NULL == getenv("LL_BAD_FMOD_OSS")) /*Flawfinder: ignore*/ { LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL; - if(mSystem->setOutput(FMOD_OUTPUTTYPE_OSS) == FMOD_OK && + if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_OSS) == FMOD_OK) && (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) { LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL; @@ -397,20 +397,22 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) // We're interested in logging which output method we // ended up with, for QA purposes. FMOD_OUTPUTTYPE output_type; - mSystem->getOutput(&output_type); - switch (output_type) + if(!Check_FMOD_Error(mSystem->getOutput(&output_type), "FMOD::System::getOutput")) { - case FMOD_OUTPUTTYPE_NOSOUND: - LL_INFOS("AppInit") << "Audio output: NoSound" << LL_ENDL; break; - case FMOD_OUTPUTTYPE_PULSEAUDIO: - LL_INFOS("AppInit") << "Audio output: PulseAudio" << LL_ENDL; break; - case FMOD_OUTPUTTYPE_ALSA: - LL_INFOS("AppInit") << "Audio output: ALSA" << LL_ENDL; break; - case FMOD_OUTPUTTYPE_OSS: - LL_INFOS("AppInit") << "Audio output: OSS" << LL_ENDL; break; - default: - LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break; - }; + switch (output_type) + { + case FMOD_OUTPUTTYPE_NOSOUND: + LL_INFOS("AppInit") << "Audio output: NoSound" << LL_ENDL; break; + case FMOD_OUTPUTTYPE_PULSEAUDIO: + LL_INFOS("AppInit") << "Audio output: PulseAudio" << LL_ENDL; break; + case FMOD_OUTPUTTYPE_ALSA: + LL_INFOS("AppInit") << "Audio output: ALSA" << LL_ENDL; break; + case FMOD_OUTPUTTYPE_OSS: + LL_INFOS("AppInit") << "Audio output: OSS" << LL_ENDL; break; + default: + LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break; + }; + } #else // LL_LINUX // initialize the FMOD engine @@ -434,10 +436,10 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) if (mEnableProfiler) { - mSystem->createChannelGroup("None", &mChannelGroups[AUDIO_TYPE_NONE]); - mSystem->createChannelGroup("SFX", &mChannelGroups[AUDIO_TYPE_SFX]); - mSystem->createChannelGroup("UI", &mChannelGroups[AUDIO_TYPE_UI]); - mSystem->createChannelGroup("Ambient", &mChannelGroups[AUDIO_TYPE_AMBIENT]); + Check_FMOD_Error(mSystem->createChannelGroup("None", &mChannelGroups[AUDIO_TYPE_NONE]), "FMOD::System::createChannelGroup"); + Check_FMOD_Error(mSystem->createChannelGroup("SFX", &mChannelGroups[AUDIO_TYPE_SFX]), "FMOD::System::createChannelGroup"); + Check_FMOD_Error(mSystem->createChannelGroup("UI", &mChannelGroups[AUDIO_TYPE_UI]), "FMOD::System::createChannelGroup"); + Check_FMOD_Error(mSystem->createChannelGroup("Ambient", &mChannelGroups[AUDIO_TYPE_AMBIENT]), "FMOD::System::createChannelGroup"); } // set up our favourite FMOD-native streaming audio implementation if none has already been added @@ -450,32 +452,37 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) unsigned int r_bufferlength; char r_name[256]; FMOD_SPEAKERMODE speaker_mode; - mSystem->getDSPBufferSize(&r_bufferlength, &r_numbuffers); - mSystem->getSoftwareFormat(&r_samplerate, &speaker_mode, NULL); - mSystem->getDriverInfo(0, r_name, 255, NULL, NULL, &speaker_mode, NULL); - std::string speaker_mode_str = "unknown"; - switch(speaker_mode) + if (!Check_FMOD_Error(mSystem->getDSPBufferSize(&r_bufferlength, &r_numbuffers), "FMOD::System::getDSPBufferSize") && + !Check_FMOD_Error(mSystem->getSoftwareFormat(&r_samplerate, &speaker_mode, NULL), "FMOD::System::getSoftwareFormat") && + !Check_FMOD_Error(mSystem->getDriverInfo(0, r_name, 255, NULL, NULL, &speaker_mode, NULL), "FMOD::System::getDriverInfo")) { - #define SPEAKER_MODE_CASE(MODE) case MODE: speaker_mode_str = #MODE; break; - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_RAW) - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_MONO) - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_STEREO) - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_QUAD) - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_SURROUND) - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_5POINT1) - SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_7POINT1) - default:; - #undef SPEAKER_MODE_CASE + std::string speaker_mode_str = "unknown"; + switch(speaker_mode) + { + #define SPEAKER_MODE_CASE(MODE) case MODE: speaker_mode_str = #MODE; break; + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_RAW) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_MONO) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_STEREO) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_QUAD) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_SURROUND) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_5POINT1) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_7POINT1) + default:; + #undef SPEAKER_MODE_CASE + } + + r_name[255] = '\0'; + int latency = 1000.0 * r_bufferlength * r_numbuffers /r_samplerate; + + LL_INFOS("AppInit") << "FMOD device: "<< r_name << "\n" + << "Output mode: "<< speaker_mode_str << "\n" + << "FMOD Studio parameters: " << r_samplerate << " Hz * " <<" bit\n" + << "\tbuffer " << r_bufferlength << " * " << r_numbuffers << " (" << latency <<"ms)" << LL_ENDL; + } + else + { + LL_WARNS("AppInit") << "Failed to retrieve FMOD device info!" << LL_ENDL; } - - r_name[255] = '\0'; - int latency = 1000.0 * r_bufferlength * r_numbuffers /r_samplerate; - - LL_INFOS("AppInit") << "FMOD device: "<< r_name << "\n" - << "Output mode: "<< speaker_mode_str << "\n" - << "FMOD Studio parameters: " << r_samplerate << " Hz * " <<" bit\n" - << "\tbuffer " << r_bufferlength << " * " << r_numbuffers << " (" << latency <<"ms)" << LL_ENDL; - mInited = true; return true; @@ -516,9 +523,9 @@ void LLAudioEngine_FMODSTUDIO::shutdown() if ( mSystem ) // speculative fix for MAINT-2657 { LL_INFOS("AudioImpl") << "LLAudioEngine_FMODSTUDIO::shutdown() Requesting FMOD Studio system closure" << LL_ENDL; - mSystem->close(); + Check_FMOD_Error(mSystem->close(), "FMOD::System::close"); LL_INFOS("AudioImpl") << "LLAudioEngine_FMODSTUDIO::shutdown() Requesting FMOD Studio system release" << LL_ENDL; - mSystem->release(); + Check_FMOD_Error(mSystem->release(), "FMOD::System::release"); } LL_INFOS("AudioImpl") << "LLAudioEngine_FMODSTUDIO::shutdown() done closing FMOD Studio" << LL_ENDL; @@ -542,34 +549,30 @@ bool LLAudioEngine_FMODSTUDIO::initWind() { mNextWindUpdate = 0.0; - if (!mWindDSP) - { - FMOD_DSP_DESCRIPTION dspdesc; - memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION)); //Set everything to zero - dspdesc.pluginsdkversion = FMOD_PLUGIN_SDK_VERSION; - strncpy(dspdesc.name,"Wind Unit", sizeof(dspdesc.name)); //Set name to "Wind Unit" - dspdesc.numoutputbuffers = 1; - dspdesc.read = &windCallback; //Assign callback. - if(Check_FMOD_Error(mSystem->createDSP(&dspdesc, &mWindDSP), "FMOD::createDSP")) - return false; + cleanupWind(); - if(mWindGen) - delete mWindGen; + FMOD_DSP_DESCRIPTION dspdesc; + memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION)); //Set everything to zero + dspdesc.pluginsdkversion = FMOD_PLUGIN_SDK_VERSION; + strncpy(dspdesc.name,"Wind Unit", sizeof(dspdesc.name)); //Set name to "Wind Unit" + dspdesc.numoutputbuffers = 1; + dspdesc.read = &windCallback; //Assign callback. + if (Check_FMOD_Error(mSystem->createDSP(&dspdesc, &mWindDSP), "FMOD::createDSP") || !mWindDSP) + return false; - int frequency = 44100; - mSystem->getSoftwareFormat(&frequency, NULL, NULL); + int frequency = 44100; + if (!Check_FMOD_Error(mSystem->getSoftwareFormat(&frequency, NULL, NULL), "FMOD::System::getSoftwareFormat")) + { mWindGen = new LLWindGen((U32)frequency); - mWindDSP->setUserData((void*)mWindGen); + FMOD_SPEAKERMODE mode; + if (!Check_FMOD_Error(mWindDSP->setUserData((void*)mWindGen), "FMOD::DSP::setUserData") && + !Check_FMOD_Error(mSystem->playDSP(mWindDSP, NULL, false, 0), "FMOD::System::playDSP") && + !Check_FMOD_Error(mSystem->getSoftwareFormat(NULL, &mode, NULL), "FMOD::System::getSoftwareFormat") && + !Check_FMOD_Error(mWindDSP->setChannelFormat(FMOD_CHANNELMASK_STEREO, 2, mode), "FMOD::DSP::setChannelFormat")) + return true; //Success } - if (mWindDSP) - { - mSystem->playDSP(mWindDSP, NULL, false, 0); - FMOD_SPEAKERMODE mode; - mSystem->getSoftwareFormat(NULL, &mode, NULL); - mWindDSP->setChannelFormat(FMOD_CHANNELMASK_STEREO, 2, mode); - return true; - } + cleanupWind(); return false; } @@ -578,7 +581,10 @@ void LLAudioEngine_FMODSTUDIO::cleanupWind() { if (mWindDSP) { - mWindDSP->release(); + FMOD::ChannelGroup* mastergroup = NULL; + if (!Check_FMOD_Error(mSystem->getMasterChannelGroup(&mastergroup), "FMOD::System::getMasterChannelGroup") && mastergroup) + Check_FMOD_Error(mastergroup->removeDSP(mWindDSP), "FMOD::ChannelGroup::removeDSP"); + Check_FMOD_Error(mWindDSP->release(), "FMOD::DSP::release"); mWindDSP = NULL; } @@ -630,8 +636,9 @@ void LLAudioEngine_FMODSTUDIO::setInternalGain(F32 gain) gain = llclamp( gain, 0.0f, 1.0f ); FMOD::ChannelGroup *master_group; - mSystem->getMasterChannelGroup(&master_group); - + if(Check_FMOD_Error(mSystem->getMasterChannelGroup(&master_group), "FMOD::System::getMasterChannelGroup")) + return; + master_group->setVolume(gain); LLStreamingAudioInterface *saimpl = getStreamingAudioImpl(); @@ -999,21 +1006,20 @@ void LLAudioChannelFMODSTUDIO::set3DMode(bool use3d) } -FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *originalbuffer, float *newbuffer, unsigned int length, int inchannels, int *outchannels) +FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels) { - // originalbuffer = fmod's original mixbuffer. - // newbuffer = the buffer passed from the previous DSP unit. - // length = length in samples at this mix time. - // userdata = user parameter passed through in FSOUND_DSP_Create. + // inbuffer = incomming data. + // newbuffer = outgoing data. AKA this DSP's output. + // length = length in samples at this mix time. True buffer size, in bytes, would be (length * sizeof(float) * inchannels). + // userdata = user-provided data attached this DSP via FMOD::DSP::setUserData. LLWindGen *windgen; FMOD::DSP *thisdsp = (FMOD::DSP *)dsp_state->instance; thisdsp->getUserData((void **)&windgen); - S32 channels, configwidth, configheight; - thisdsp->getInfo(0, 0, &channels, &configwidth, &configheight); - windgen->windGenerate((LLAudioEngine_FMODSTUDIO::MIXBUFFERFORMAT *)newbuffer, length); + if (windgen) + windgen->windGenerate((LLAudioEngine_FMODSTUDIO::MIXBUFFERFORMAT *)outbuffer, length); return FMOD_OK; } diff --git a/indra/llaudio/llstreamingaudio_fmodex.cpp b/indra/llaudio/llstreamingaudio_fmodex.cpp index 154b1fc34..9c4aad93b 100644 --- a/indra/llaudio/llstreamingaudio_fmodex.cpp +++ b/indra/llaudio/llstreamingaudio_fmodex.cpp @@ -39,6 +39,13 @@ #include "llstreamingaudio_fmodex.h" +inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string) +{ + if (result == FMOD_OK) + return false; + LL_WARNS("AudioImpl") << string << " Error: " << FMOD_ErrorString(result) << LL_ENDL; + return true; +} class LLAudioStreamManagerFMODEX { @@ -50,7 +57,7 @@ public: const std::string& getURL() { return mInternetStreamURL; } - FMOD_OPENSTATE getOpenState(unsigned int* percentbuffered=NULL, bool* starving=NULL, bool* diskbusy=NULL); + FMOD_RESULT getOpenState(FMOD_OPENSTATE& openstate, unsigned int* percentbuffered=NULL, bool* starving=NULL, bool* diskbusy=NULL); protected: FMOD::System* mSystem; FMOD::Channel* mStreamChannel; @@ -72,11 +79,14 @@ LLStreamingAudio_FMODEX::LLStreamingAudio_FMODEX(FMOD::System *system) : mGain(1.0f), mMetaData(NULL) { + FMOD_RESULT result; + // Number of milliseconds of audio to buffer for the audio card. // Must be larger than the usual Second Life frame stutter time. const U32 buffer_seconds = 10; //sec const U32 estimated_bitrate = 128; //kbit/sec - mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES); + result = mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES); + Check_FMOD_Error(result, "FMOD::System::setStreamBufferSize"); // Here's where we set the size of the network buffer and some buffering // parameters. In this case we want a network buffer of 16k, we want it @@ -200,9 +210,15 @@ void LLStreamingAudio_FMODEX::update() unsigned int progress; bool starving; bool diskbusy; - FMOD_OPENSTATE open_state = mCurrentInternetStreamp->getOpenState(&progress, &starving, &diskbusy); + FMOD_OPENSTATE open_state; + FMOD_RESULT res = mCurrentInternetStreamp->getOpenState(open_state, &progress, &starving, &diskbusy); - if (open_state == FMOD_OPENSTATE_READY) + if (res != FMOD_OK || open_state == FMOD_OPENSTATE_ERROR) + { + stop(); + return; + } + else if (open_state == FMOD_OPENSTATE_READY) { // Stream is live @@ -212,14 +228,9 @@ void LLStreamingAudio_FMODEX::update() { // Reset volume to previously set volume setGain(getGain()); - mFMODInternetStreamChannelp->setPaused(false); + Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused"); } } - else if(open_state == FMOD_OPENSTATE_ERROR) - { - stop(); - return; - } if(mFMODInternetStreamChannelp) { @@ -255,7 +266,7 @@ void LLStreamingAudio_FMODEX::update() if (!LLStringUtil::compareInsensitive(name, "Sample Rate Change")) { llinfos << "Stream forced changing sample rate to " << *((float *)tag.data) << llendl; - mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data)); + Check_FMOD_Error(mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data)), "FMOD::Channel::setFrequency"); } continue; default: @@ -321,18 +332,17 @@ void LLStreamingAudio_FMODEX::update() if(starving) { bool paused = false; - mFMODInternetStreamChannelp->getPaused(&paused); - if(!paused) + if (mFMODInternetStreamChannelp->getPaused(&paused) == FMOD_OK && !paused) { llinfos << "Stream starvation detected! Pausing stream until buffer nearly full." << llendl; llinfos << " (diskbusy="<setPaused(true); + Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(true), "FMOD::Channel::setPaused"); } } else if(progress > 80) { - mFMODInternetStreamChannelp->setPaused(false); + Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused"); } } } @@ -349,8 +359,8 @@ void LLStreamingAudio_FMODEX::stop() } if (mFMODInternetStreamChannelp) { - mFMODInternetStreamChannelp->setPaused(true); - mFMODInternetStreamChannelp->setPriority(0); + Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(true), "FMOD::Channel::setPaused"); + Check_FMOD_Error(mFMODInternetStreamChannelp->setPriority(0), "FMOD::Channel::setPriority"); mFMODInternetStreamChannelp = NULL; } @@ -431,7 +441,7 @@ void LLStreamingAudio_FMODEX::setGain(F32 vol) { vol = llclamp(vol * vol, 0.f, 1.f); //should vol be squared here? - mFMODInternetStreamChannelp->setVolume(vol); + Check_FMOD_Error(mFMODInternetStreamChannelp->setVolume(vol), "FMOD::Channel::setVolume"); } } @@ -441,8 +451,8 @@ void LLStreamingAudio_FMODEX::setGain(F32 vol) return false; bool muted=false; - mFMODInternetStreamChannelp->getMute(&muted); - if(muted) + FMOD_RESULT res = mFMODInternetStreamChannelp->getMute(&muted); + if(res != FMOD_OK || muted) return false; static std::vector local_array(count); //Have to have an extra buffer to mix channels. Bleh. @@ -495,7 +505,8 @@ LLAudioStreamManagerFMODEX::LLAudioStreamManagerFMODEX(FMOD::System *system, con FMOD::Channel *LLAudioStreamManagerFMODEX::startStream() { // We need a live and opened stream before we try and play it. - if (!mInternetStream || getOpenState() != FMOD_OPENSTATE_READY) + FMOD_OPENSTATE open_state; + if (getOpenState(open_state) != FMOD_OK || open_state != FMOD_OPENSTATE_READY) { llwarns << "No internet stream to start playing!" << llendl; return NULL; @@ -504,7 +515,7 @@ FMOD::Channel *LLAudioStreamManagerFMODEX::startStream() if(mStreamChannel) return mStreamChannel; //Already have a channel for this stream. - mSystem->playSound(FMOD_CHANNEL_FREE, mInternetStream, true, &mStreamChannel); + Check_FMOD_Error(mSystem->playSound(FMOD_CHANNEL_FREE, mInternetStream, true, &mStreamChannel), "FMOD::System::playSound"); return mStreamChannel; } @@ -513,17 +524,17 @@ bool LLAudioStreamManagerFMODEX::stopStream() if (mInternetStream) { bool close = true; - switch (getOpenState()) + FMOD_OPENSTATE open_state; + if (getOpenState(open_state) == FMOD_OK) { - case FMOD_OPENSTATE_CONNECTING: - close = false; - break; - /*case FSOUND_STREAM_NET_NOTCONNECTED: - case FSOUND_STREAM_NET_BUFFERING: - case FSOUND_STREAM_NET_READY: - case FSOUND_STREAM_NET_ERROR:*/ - default: - close = true; + switch (open_state) + { + case FMOD_OPENSTATE_CONNECTING: + close = false; + break; + default: + close = true; + } } if (close && mInternetStream->release() == FMOD_OK) @@ -543,21 +554,23 @@ bool LLAudioStreamManagerFMODEX::stopStream() } } -FMOD_OPENSTATE LLAudioStreamManagerFMODEX::getOpenState(unsigned int* percentbuffered, bool* starving, bool* diskbusy) +FMOD_RESULT LLAudioStreamManagerFMODEX::getOpenState(FMOD_OPENSTATE& state, unsigned int* percentbuffered, bool* starving, bool* diskbusy) { - FMOD_OPENSTATE state; - mInternetStream->getOpenState(&state,percentbuffered,starving,diskbusy); - return state; + if (!mInternetStream) + return FMOD_ERR_INVALID_HANDLE; + FMOD_RESULT result = mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy); + Check_FMOD_Error(result, "FMOD::Sound::getOpenState"); + return result; } void LLStreamingAudio_FMODEX::setBufferSizes(U32 streambuffertime, U32 decodebuffertime) { - mSystem->setStreamBufferSize(streambuffertime/1000*128*128, FMOD_TIMEUNIT_RAWBYTES); + Check_FMOD_Error(mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES), "FMOD::System::setStreamBufferSize"); FMOD_ADVANCEDSETTINGS settings; memset(&settings,0,sizeof(settings)); - settings.cbsize=sizeof(settings); + settings.cbSize=sizeof(settings); settings.defaultDecodeBufferSize = decodebuffertime;//ms - mSystem->setAdvancedSettings(&settings); + Check_FMOD_Error(mSystem->setAdvancedSettings(&settings), "FMOD::System::setAdvancedSettings"); } bool LLStreamingAudio_FMODEX::releaseDeadStreams() diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.cpp b/indra/llaudio/llstreamingaudio_fmodstudio.cpp index cfe8c05c3..dbfaaa48d 100644 --- a/indra/llaudio/llstreamingaudio_fmodstudio.cpp +++ b/indra/llaudio/llstreamingaudio_fmodstudio.cpp @@ -58,7 +58,7 @@ public: const std::string& getURL() { return mInternetStreamURL; } - FMOD_OPENSTATE getOpenState(unsigned int* percentbuffered=NULL, bool* starving=NULL, bool* diskbusy=NULL); + FMOD_RESULT getOpenState(FMOD_OPENSTATE& openstate, unsigned int* percentbuffered = NULL, bool* starving = NULL, bool* diskbusy = NULL); protected: FMOD::System* mSystem; FMOD::Channel* mStreamChannel; @@ -121,7 +121,9 @@ LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) : mCurrentInternetStreamp(NULL), mFMODInternetStreamChannelp(NULL), mGain(1.0f), - mMetaData(NULL) + mMetaData(NULL), + mStreamGroup(NULL), + mStreamDSP(NULL) { FMOD_RESULT result; @@ -140,8 +142,7 @@ LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) : // Leave the net buffer properties at the default. //FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80); - result = system->createChannelGroup("stream", &mStreamGroup); - Check_FMOD_Error(result,"FMOD::System::createChannelGroup"); + Check_FMOD_Error(system->createChannelGroup("stream", &mStreamGroup), "FMOD::System::createChannelGroup"); FMOD_DSP_DESCRIPTION dspdesc; memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION)); //Zero out everything @@ -150,11 +151,7 @@ LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) : dspdesc.numoutputbuffers = 1; dspdesc.read = &waveDataCallback; //Assign callback. - result = system->createDSP(&dspdesc, &mStreamDSP); - Check_FMOD_Error(result, "FMOD::System::createDSPByType"); - result = mStreamGroup->addDSP(FMOD_CHANNELCONTROL_DSP_TAIL, mStreamDSP); - Check_FMOD_Error(result, "FMOD::ChannelGroup::addDSP"); - mStreamDSP->setActive(false); + Check_FMOD_Error(system->createDSP(&dspdesc, &mStreamDSP), "FMOD::System::createDSP"); } @@ -168,14 +165,7 @@ LLStreamingAudio_FMODSTUDIO::~LLStreamingAudio_FMODSTUDIO() ms_sleep(10); } - if (mStreamGroup) - { - if (mStreamDSP) - mStreamGroup->removeDSP(mStreamDSP); - mStreamGroup->release(); - } - if (mStreamDSP) - mStreamDSP->release(); + cleanupWaveData(); } @@ -279,9 +269,15 @@ void LLStreamingAudio_FMODSTUDIO::update() unsigned int progress; bool starving; bool diskbusy; - FMOD_OPENSTATE open_state = mCurrentInternetStreamp->getOpenState(&progress, &starving, &diskbusy); + FMOD_OPENSTATE open_state; + FMOD_RESULT result = mCurrentInternetStreamp->getOpenState(open_state, &progress, &starving, &diskbusy); - if (open_state == FMOD_OPENSTATE_READY) + if (result != FMOD_OK || open_state == FMOD_OPENSTATE_ERROR) + { + stop(); + return; + } + else if (open_state == FMOD_OPENSTATE_READY) { // Stream is live @@ -292,15 +288,13 @@ void LLStreamingAudio_FMODSTUDIO::update() // Reset volume to previously set volume setGain(getGain()); if (mStreamDSP) - mStreamDSP->setActive(true); - mFMODInternetStreamChannelp->setPaused(false); + { + Check_FMOD_Error(mFMODInternetStreamChannelp->addDSP(FMOD_CHANNELCONTROL_DSP_TAIL, mStreamDSP), "FMOD::Channel::addDSP"); + } + Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused"); } } - else if(open_state == FMOD_OPENSTATE_ERROR) - { - stop(); - return; - } + if(mFMODInternetStreamChannelp) { @@ -336,7 +330,7 @@ void LLStreamingAudio_FMODSTUDIO::update() if (!LLStringUtil::compareInsensitive(name, "Sample Rate Change")) { llinfos << "Stream forced changing sample rate to " << *((float *)tag.data) << llendl; - mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data)); + Check_FMOD_Error(mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data)), "FMOD::Channel::setFrequency"); } continue; default: @@ -402,18 +396,17 @@ void LLStreamingAudio_FMODSTUDIO::update() if(starving) { bool paused = false; - mFMODInternetStreamChannelp->getPaused(&paused); - if(!paused) + if (mFMODInternetStreamChannelp->getPaused(&paused) == FMOD_OK && !paused) { llinfos << "Stream starvation detected! Pausing stream until buffer nearly full." << llendl; llinfos << " (diskbusy="<setPaused(true); + Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(true), "FMOD::Channel::setPaused"); } } else if(progress > 80) { - mFMODInternetStreamChannelp->setPaused(false); + Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused"); } } } @@ -429,18 +422,14 @@ void LLStreamingAudio_FMODSTUDIO::stop() mMetaData = NULL; } - if (mStreamDSP) - { - mSystem->lockDSP(); - mStreamDSP->setActive(false); - gWaveDataBufferSize = 0; - mSystem->unlockDSP(); - } - if (mFMODInternetStreamChannelp) { - mFMODInternetStreamChannelp->setPaused(true); - mFMODInternetStreamChannelp->setPriority(0); + Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(true), "FMOD::Channel::setPaused"); + Check_FMOD_Error(mFMODInternetStreamChannelp->setPriority(0), "FMOD::Channel::setPriority"); + if (mStreamDSP) + { + Check_FMOD_Error(mFMODInternetStreamChannelp->removeDSP(mStreamDSP), "FMOD::Channel::removeDSP"); + } mFMODInternetStreamChannelp = NULL; } @@ -521,7 +510,7 @@ void LLStreamingAudio_FMODSTUDIO::setGain(F32 vol) { vol = llclamp(vol * vol, 0.f, 1.f); //should vol be squared here? - mFMODInternetStreamChannelp->setVolume(vol); + Check_FMOD_Error(mFMODInternetStreamChannelp->setVolume(vol), "FMOD::Channel::setVolume"); } } @@ -534,8 +523,8 @@ void LLStreamingAudio_FMODSTUDIO::setGain(F32 vol) return false; bool muted = false; - mFMODInternetStreamChannelp->getMute(&muted); - if(muted) + FMOD_RESULT res = mFMODInternetStreamChannelp->getMute(&muted); + if(res != FMOD_OK || muted) return false; { U32 buff_size; @@ -582,7 +571,8 @@ LLAudioStreamManagerFMODSTUDIO::LLAudioStreamManagerFMODSTUDIO(FMOD::System *sys FMOD::Channel *LLAudioStreamManagerFMODSTUDIO::startStream() { // We need a live and opened stream before we try and play it. - if (!mInternetStream || getOpenState() != FMOD_OPENSTATE_READY) + FMOD_OPENSTATE open_state; + if (getOpenState(open_state) != FMOD_OK || open_state != FMOD_OPENSTATE_READY) { llwarns << "No internet stream to start playing!" << llendl; return NULL; @@ -591,7 +581,7 @@ FMOD::Channel *LLAudioStreamManagerFMODSTUDIO::startStream() if(mStreamChannel) return mStreamChannel; //Already have a channel for this stream. - mSystem->playSound(mInternetStream, mChannelGroup, true, &mStreamChannel); + Check_FMOD_Error(mSystem->playSound(mInternetStream, mChannelGroup, true, &mStreamChannel), "FMOD::System::playSound"); return mStreamChannel; } @@ -600,13 +590,17 @@ bool LLAudioStreamManagerFMODSTUDIO::stopStream() if (mInternetStream) { bool close = true; - switch (getOpenState()) + FMOD_OPENSTATE open_state; + if (getOpenState(open_state) == FMOD_OK) { - case FMOD_OPENSTATE_CONNECTING: - close = false; - break; - default: - close = true; + switch (open_state) + { + case FMOD_OPENSTATE_CONNECTING: + close = false; + break; + default: + close = true; + } } if (close && mInternetStream->release() == FMOD_OK) @@ -626,21 +620,23 @@ bool LLAudioStreamManagerFMODSTUDIO::stopStream() } } -FMOD_OPENSTATE LLAudioStreamManagerFMODSTUDIO::getOpenState(unsigned int* percentbuffered, bool* starving, bool* diskbusy) +FMOD_RESULT LLAudioStreamManagerFMODSTUDIO::getOpenState(FMOD_OPENSTATE& state, unsigned int* percentbuffered, bool* starving, bool* diskbusy) { - FMOD_OPENSTATE state; - mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy); - return state; + if (!mInternetStream) + return FMOD_ERR_INVALID_HANDLE; + FMOD_RESULT result = mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy); + Check_FMOD_Error(result, "FMOD::Sound::getOpenState"); + return result; } void LLStreamingAudio_FMODSTUDIO::setBufferSizes(U32 streambuffertime, U32 decodebuffertime) { - mSystem->setStreamBufferSize(streambuffertime/1000*128*128, FMOD_TIMEUNIT_RAWBYTES); + Check_FMOD_Error(mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES), "FMOD::System::setStreamBufferSize"); FMOD_ADVANCEDSETTINGS settings; memset(&settings,0,sizeof(settings)); settings.cbSize=sizeof(settings); settings.defaultDecodeBufferSize = decodebuffertime;//ms - mSystem->setAdvancedSettings(&settings); + Check_FMOD_Error(mSystem->setAdvancedSettings(&settings), "FMOD::System::setAdvancedSettings"); } bool LLStreamingAudio_FMODSTUDIO::releaseDeadStreams() @@ -663,4 +659,17 @@ bool LLStreamingAudio_FMODSTUDIO::releaseDeadStreams() } return mDeadStreams.empty(); +} + +void LLStreamingAudio_FMODSTUDIO::cleanupWaveData() +{ + if (mStreamGroup) + { + Check_FMOD_Error(mStreamGroup->release(), "FMOD::ChannelGroup::release"); + mStreamGroup = NULL; + } + + if(mStreamDSP) + Check_FMOD_Error(mStreamDSP->release(), "FMOD::DSP::release"); + mStreamDSP = NULL; } \ No newline at end of file diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.h b/indra/llaudio/llstreamingaudio_fmodstudio.h index 831e2407e..bb4a77e42 100644 --- a/indra/llaudio/llstreamingaudio_fmodstudio.h +++ b/indra/llaudio/llstreamingaudio_fmodstudio.h @@ -75,6 +75,7 @@ class LLStreamingAudio_FMODSTUDIO : public LLStreamingAudioInterface private: bool releaseDeadStreams(); + void cleanupWaveData(); FMOD::System *mSystem; From 36a8a204343387e66e6039da1a29ec7a0c72ba09 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Wed, 26 Nov 2014 22:53:32 -0600 Subject: [PATCH 18/55] Alignment fixes. --- indra/llappearance/llavatarjoint.h | 10 ++++++++++ indra/llappearance/llpolymorph.h | 10 ++++++++++ indra/llcharacter/lleditingmotion.h | 10 ++++++++++ indra/llcharacter/llkeyframestandmotion.h | 10 ++++++++++ indra/llcharacter/llpose.h | 10 ++++++++++ indra/newview/llfloaterexploreanimations.h | 10 ++++++++++ indra/newview/llviewerjoint.h | 10 ++++++++++ indra/newview/llviewerjointattachment.h | 10 ++++++++++ indra/newview/llviewerjointmesh.h | 10 ++++++++++ 9 files changed, 90 insertions(+) diff --git a/indra/llappearance/llavatarjoint.h b/indra/llappearance/llavatarjoint.h index fec91503c..e4da9bb9a 100644 --- a/indra/llappearance/llavatarjoint.h +++ b/indra/llappearance/llavatarjoint.h @@ -127,6 +127,16 @@ public: LLAvatarJointCollisionVolume(); virtual ~LLAvatarJointCollisionVolume() {}; + void* operator new(size_t size) + { + return ll_aligned_malloc_16(size); + } + + void operator delete(void* ptr) + { + ll_aligned_free_16(ptr); + } + /*virtual*/ BOOL inheritScale() { return TRUE; } /*virtual*/ U32 render( F32 pixelArea, BOOL first_pass = TRUE, BOOL is_dummy = FALSE ); diff --git a/indra/llappearance/llpolymorph.h b/indra/llappearance/llpolymorph.h index f1ecef881..b00fef4ac 100644 --- a/indra/llappearance/llpolymorph.h +++ b/indra/llappearance/llpolymorph.h @@ -163,6 +163,16 @@ public: LLPolyMorphTarget(LLPolyMesh *poly_mesh); ~LLPolyMorphTarget(); + void* operator new(size_t size) + { + return ll_aligned_malloc_16(size); + } + + void operator delete(void* ptr) + { + ll_aligned_free_16(ptr); + } + // Special: These functions are overridden by child classes LLPolyMorphTargetInfo* getInfo() const { return (LLPolyMorphTargetInfo*)mInfo; } // This sets mInfo and calls initialization functions diff --git a/indra/llcharacter/lleditingmotion.h b/indra/llcharacter/lleditingmotion.h index bc95ff682..b06d2b774 100644 --- a/indra/llcharacter/lleditingmotion.h +++ b/indra/llcharacter/lleditingmotion.h @@ -58,6 +58,16 @@ public: // Destructor virtual ~LLEditingMotion(); + void* operator new(size_t size) + { + return ll_aligned_malloc_16(size); + } + + void operator delete(void* ptr) + { + ll_aligned_free_16(ptr); + } + public: //------------------------------------------------------------------------- // functions to support MotionController and MotionRegistry diff --git a/indra/llcharacter/llkeyframestandmotion.h b/indra/llcharacter/llkeyframestandmotion.h index 346df97f0..c3a9ddd22 100644 --- a/indra/llcharacter/llkeyframestandmotion.h +++ b/indra/llcharacter/llkeyframestandmotion.h @@ -53,6 +53,16 @@ public: // Destructor virtual ~LLKeyframeStandMotion(); + void* operator new(size_t size) + { + return ll_aligned_malloc_16(size); + } + + void operator delete(void* ptr) + { + ll_aligned_free_16(ptr); + } + public: //------------------------------------------------------------------------- // functions to support MotionController and MotionRegistry diff --git a/indra/llcharacter/llpose.h b/indra/llcharacter/llpose.h index 2b976b219..63c0fdde4 100644 --- a/indra/llcharacter/llpose.h +++ b/indra/llcharacter/llpose.h @@ -96,6 +96,16 @@ protected: public: LLJointStateBlender(); ~LLJointStateBlender(); + void* operator new(size_t size) + { + return ll_aligned_malloc_16(size); + } + + void operator delete(void* ptr) + { + ll_aligned_free_16(ptr); + } + void blendJointStates(BOOL apply_now = TRUE); BOOL addJointState(const LLPointer& joint_state, S32 priority, BOOL additive_blend); void interpolate(F32 u); diff --git a/indra/newview/llfloaterexploreanimations.h b/indra/newview/llfloaterexploreanimations.h index bb3b91469..b0ec7f8e2 100644 --- a/indra/newview/llfloaterexploreanimations.h +++ b/indra/newview/llfloaterexploreanimations.h @@ -27,6 +27,16 @@ public: void update(); + void* operator new(size_t size) + { + return ll_aligned_malloc_16(size); + } + + void operator delete(void* ptr) + { + ll_aligned_free_16(ptr); + } + private: virtual ~LLFloaterExploreAnimations(); diff --git a/indra/newview/llviewerjoint.h b/indra/newview/llviewerjoint.h index fd262b6e8..1ec87ee41 100644 --- a/indra/newview/llviewerjoint.h +++ b/indra/newview/llviewerjoint.h @@ -49,6 +49,16 @@ public: LLViewerJoint(const std::string &name, LLJoint *parent = NULL); virtual ~LLViewerJoint(); + void* operator new(size_t size) + { + return ll_aligned_malloc_16(size); + } + + void operator delete(void* ptr) + { + ll_aligned_free_16(ptr); + } + // Render character hierarchy. // Traverses the entire joint hierarchy, setting up // transforms and calling the drawShape(). diff --git a/indra/newview/llviewerjointattachment.h b/indra/newview/llviewerjointattachment.h index 9addafaee..bb94c3678 100644 --- a/indra/newview/llviewerjointattachment.h +++ b/indra/newview/llviewerjointattachment.h @@ -47,6 +47,16 @@ public: LLViewerJointAttachment(); virtual ~LLViewerJointAttachment(); + void* operator new(size_t size) + { + return ll_aligned_malloc_16(size); + } + + void operator delete(void* ptr) + { + ll_aligned_free_16(ptr); + } + //virtual U32 render( F32 pixelArea ); // Returns triangle count // Returns true if this object is transparent. diff --git a/indra/newview/llviewerjointmesh.h b/indra/newview/llviewerjointmesh.h index c5b872ede..017a7fe11 100644 --- a/indra/newview/llviewerjointmesh.h +++ b/indra/newview/llviewerjointmesh.h @@ -50,6 +50,16 @@ public: // Destructor virtual ~LLViewerJointMesh(); + void* operator new(size_t size) + { + return ll_aligned_malloc_16(size); + } + + void operator delete(void* ptr) + { + ll_aligned_free_16(ptr); + } + // Render time method to upload batches of joint matrices void uploadJointMatrices(); From fedd094987bbbc9528adaa7ad0ba5223d260dc56 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Wed, 26 Nov 2014 22:57:50 -0600 Subject: [PATCH 19/55] Clean up warning. (implicit float to signed integer conversion) --- indra/llmath/llmath.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index 32fefad4d..5009a1c4f 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -211,7 +211,7 @@ namespace llmath inline S32 llround(const F32 val) { #if __cplusplus >= 201103L || _MSC_VER >= 1800 - return std::round(val); + return S32(std::round(val)); #else return llfloor(val + 0.5f); #endif From 7ddef751ef30e9f93c42cda383ffaac994686440 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Fri, 28 Nov 2014 23:29:34 -0600 Subject: [PATCH 20/55] Update md5 checksums for vs2012 x64 libs. --- install.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/install.xml b/install.xml index e4f683971..1760d1f4f 100644 --- a/install.xml +++ b/install.xml @@ -67,7 +67,7 @@ windows64 md5sum - 54684a0e539879c4e20839929faca547 + 3ed28129c186d0f531701e1aad035d15 url https://bitbucket.org/SingularityViewer/libraries/downloads/glod-1.0pre4-windows64-vs12-20140723.tar.bz2 @@ -136,7 +136,7 @@ windows64 md5sum - 1a3dbaf92136ca05e57c92c9c09cbff8 + dddc81e3a8c3914510ae90af9b9a429d url https://bitbucket.org/SingularityViewer/libraries/downloads/apr_suite-1.4.5-windows64-vs12-20140710.tar.bz2 @@ -183,7 +183,7 @@ windows64 md5sum - b65ca4ce2e9cc34f3ac545ba3bfbdb3a + 9af96e2c7a819d770146917afb4ff481 url https://bitbucket.org/SingularityViewer/libraries/downloads/ares-1.10.0-windows64-vs12-20140709.tar.bz2 @@ -445,7 +445,7 @@ windows64 md5sum - b68a9abb802abf52f63c805a589eb3fc + fa21aba89409f5959a69c8cc5807103e url https://bitbucket.org/SingularityViewer/libraries/downloads/expat-2.1.0-windows64-vs12-20140709.tar.bz2 @@ -514,7 +514,7 @@ windows64 md5sum - 4a33389ffa875bfd4554fa4d8198692c + 3049465928bf2da496b19e53a03fcbde url https://bitbucket.org/SingularityViewer/libraries/downloads/freetype-2.5.3-windows64-vs12-20140725.tar.bz2 @@ -818,7 +818,7 @@ windows64 md5sum - d687c074b621715499bfe6e9ba1acbd2 + c2326faf530ea31391843a8c5aa8ccfe url https://bitbucket.org/SingularityViewer/libraries/downloads/libjpeg_turbo-1.3.1-windows64-vs12-20140709.tar.bz2 @@ -1109,7 +1109,7 @@ windows64 md5sum - c0fd9371a94cd230c6076c80c7d83a1a + 10533b646d582b936b2f8d3cb32cd73c url https://bitbucket.org/SingularityViewer/libraries/downloads/libndofdev-0.1-windows64-vs12-20140710.tar.bz2 @@ -1156,7 +1156,7 @@ windows64 md5sum - d074bd556211fd29c58326b2898b0a12 + a7c94d900a2a2551423efed99fa8e29d url https://bitbucket.org/SingularityViewer/libraries/downloads/ogg_vorbis-1.3.2-1.3.4-windows64-vs12-20140709.tar.bz2 @@ -1416,7 +1416,7 @@ windows64 md5sum - 56175d424e533bbc111a32054fbd2926 + 652a2034e8a2d4cb63f77eb3cc723351 url https://bitbucket.org/SingularityViewer/libraries/downloads/xmlrpc_epi-0.54.1-windows64-vs12-20140709.tar.bz2 @@ -1463,7 +1463,7 @@ windows64 md5sum - 3f1ca95ca4461aac57e7255c42db3ebc + 59deda6dde1e6f8cb90b2285890dce82 url https://bitbucket.org/SingularityViewer/libraries/downloads/zlib-1.2.8-windows64-vs12-20140709.tar.bz2 From 958031dbafb8d71af2ec2a9fe5c423ba46f3e9da Mon Sep 17 00:00:00 2001 From: Shyotl Date: Wed, 3 Dec 2014 22:36:42 -0600 Subject: [PATCH 21/55] Moved build dirs out of indra directory. installed.xml and prebuilts are now per-build-target. Added support for v3 prebuilt package layout. --- indra/CMakeLists.txt | 18 ++ indra/cmake/APR.cmake | 27 +- indra/cmake/Audio.cmake | 5 +- indra/cmake/BerkeleyDB.cmake | 5 +- indra/cmake/Boost.cmake | 5 +- indra/cmake/CARes.cmake | 11 +- indra/cmake/CURL.cmake | 5 +- indra/cmake/Colladadom.cmake | 8 +- indra/cmake/CopyWinLibs.cmake | 328 --------------------- indra/cmake/CopyWinLibs.cmake.in | 238 +++++++++++++++ indra/cmake/DBusGlib.cmake | 5 +- indra/cmake/DownloadPrebuilt.cmake.in | 4 +- indra/cmake/ELFIO.cmake | 5 +- indra/cmake/EXPAT.cmake | 5 +- indra/cmake/FMODEX.cmake | 4 +- indra/cmake/FMODSTUDIO.cmake | 4 +- indra/cmake/FreeType.cmake | 19 +- indra/cmake/GLOD.cmake | 5 +- indra/cmake/GStreamer010Plugin.cmake | 11 +- indra/cmake/GoogleBreakpad.cmake | 7 +- indra/cmake/GooglePerfTools.cmake | 4 +- indra/cmake/Hunspell.cmake | 5 +- indra/cmake/JPEG.cmake | 9 +- indra/cmake/JsonCpp.cmake | 5 +- indra/cmake/LLWindow.cmake | 5 +- indra/cmake/Linking.cmake | 17 +- indra/cmake/NDOF.cmake | 5 +- indra/cmake/OPENAL.cmake | 5 +- indra/cmake/OpenGL.cmake | 5 +- indra/cmake/OpenSSL.cmake | 5 +- indra/cmake/PNG.cmake | 10 +- indra/cmake/PulseAudio.cmake | 5 +- indra/cmake/QuickTimePlugin.cmake | 9 +- indra/cmake/UI.cmake | 14 +- indra/cmake/Variables.cmake | 9 +- indra/cmake/WebKitLibPlugin.cmake | 4 +- indra/cmake/XmlRpcEpi.cmake | 5 +- indra/cmake/ZLIB.cmake | 5 +- indra/develop.py | 8 +- indra/newview/CMakeLists.txt | 56 ++-- indra/newview/generate_breakpad_symbols.py | 13 +- indra/newview/viewer_manifest.py | 22 +- install.xml | 12 +- scripts/install.py | 4 +- 44 files changed, 502 insertions(+), 458 deletions(-) delete mode 100644 indra/cmake/CopyWinLibs.cmake create mode 100644 indra/cmake/CopyWinLibs.cmake.in diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 8b71ccf51..d6f0e621c 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -16,6 +16,8 @@ cmake_minimum_required(VERSION 2.6.2 FATAL_ERROR) # basis to convince CMake to add the proper link directory. This line # can be removed when we use full paths for all libraries. cmake_policy(SET CMP0003 OLD) +cmake_policy(SET CMP0048 OLD) +cmake_policy(SET CMP0026 OLD) set(ROOT_PROJECT_NAME "Singularity" CACHE STRING "The root project/makefile/solution name. Defaults to Singularity.") @@ -41,6 +43,9 @@ if(NOT STANDALONE) # We prepare prebuilt binaries when not building standalone. set(prepare_depends ${CMAKE_BINARY_DIR}/prepare/prebuilt) endif(NOT STANDALONE) +if(WINDOWS) + set(prepare_depends ${prepare_depends} copy_win_libs) +endif(WINDOWS) add_custom_target(prepare DEPENDS ${prepare_depends}) add_subdirectory(cmake) @@ -107,3 +112,16 @@ add_custom_command( DEPENDS ${CMAKE_SOURCE_DIR}/../install.xml ${CMAKE_BINARY_DIR}/DownloadPrebuilt.cmake ) + + +if(WINDOWS) + configure_file(${CMAKE_SOURCE_DIR}/cmake/CopyWinLibs.cmake.in + ${CMAKE_BINARY_DIR}/CopyWinLibs.cmake @ONLY) + add_custom_command( + COMMENT "Copying prebuilt libraries to viewer executable directory" + OUTPUT ${CMAKE_BINARY_DIR}/CopyWinLibs + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/CopyWinLibs.cmake + DEPENDS ${CMAKE_BINARY_DIR}/prepare/prebuilt ${CMAKE_BINARY_DIR}/CopyWinLibs.cmake + ) + add_custom_target(copy_win_libs DEPENDS ${CMAKE_BINARY_DIR}/CopyWinLibs) +endif(WINDOWS) \ No newline at end of file diff --git a/indra/cmake/APR.cmake b/indra/cmake/APR.cmake index cdc9c32a8..c47363aa1 100644 --- a/indra/cmake/APR.cmake +++ b/indra/cmake/APR.cmake @@ -14,25 +14,25 @@ else (STANDALONE) use_prebuilt_binary(apr_suite) if (WINDOWS) set(APR_LIBRARIES - debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.lib - optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.lib + debug libapr-1.lib + optimized libapr-1.lib ) set(APRICONV_LIBRARIES - debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapriconv-1.lib - optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapriconv-1.lib + debug libapriconv-1.lib + optimized libapriconv-1.lib ) set(APRUTIL_LIBRARIES - debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.lib ${APRICONV_LIBRARIES} - optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.lib ${APRICONV_LIBRARIES} + debug libaprutil-1.lib + optimized libaprutil-1.lib ) elseif (DARWIN) set(APR_LIBRARIES - debug ${ARCH_PREBUILT_DIRS_DEBUG}/libapr-1.0.dylib - optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libapr-1.0.dylib + debug libapr-1.0.dylib + optimized libapr-1.0.dylib ) - set(APRUTIL_LIBRARIES - debug ${ARCH_PREBUILT_DIRS_DEBUG}/libaprutil-1.dylib - optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libaprutil-1.dylib + set(APRUTIL_LIBRARIES + debug libaprutil-1.dylib + optimized libaprutil-1.dylib ) set(APRICONV_LIBRARIES iconv) else (WINDOWS) @@ -40,7 +40,10 @@ else (STANDALONE) set(APRUTIL_LIBRARIES aprutil-1) set(APRICONV_LIBRARIES iconv) endif (WINDOWS) - set(APR_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/apr-1) + set(APR_INCLUDE_DIR + ${LIBS_PREBUILT_DIR}/include/apr-1 + ${LIBS_PREBUILT_LEGACY_DIR}/include/apr-1 + ) if (LINUX) list(APPEND APRUTIL_LIBRARIES ${DB_LIBRARIES}) diff --git a/indra/cmake/Audio.cmake b/indra/cmake/Audio.cmake index 3d78e6751..27633ff72 100644 --- a/indra/cmake/Audio.cmake +++ b/indra/cmake/Audio.cmake @@ -9,7 +9,10 @@ if (STANDALONE) pkg_check_modules(VORBISFILE REQUIRED vorbisfile) else (STANDALONE) use_prebuilt_binary(ogg-vorbis) - set(VORBIS_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + set(VORBIS_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) set(VORBISENC_INCLUDE_DIRS ${VORBIS_INCLUDE_DIRS}) set(VORBISFILE_INCLUDE_DIRS ${VORBIS_INCLUDE_DIRS}) diff --git a/indra/cmake/BerkeleyDB.cmake b/indra/cmake/BerkeleyDB.cmake index 032dd510e..5b885c6a7 100644 --- a/indra/cmake/BerkeleyDB.cmake +++ b/indra/cmake/BerkeleyDB.cmake @@ -13,5 +13,8 @@ else (STANDALONE) else (LINUX) set(DB_LIBRARIES db-4.2) endif (LINUX) - set(DB_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + set(DB_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) endif (STANDALONE) diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake index b1c6606dc..0421b1c9e 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -11,7 +11,10 @@ if (STANDALONE) find_package(Boost 1.51.0 COMPONENTS date_time filesystem program_options regex system thread wave context) else (STANDALONE) use_prebuilt_binary(boost) - set(Boost_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + set(Boost_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) set(Boost_VERSION "1.52") if (WINDOWS) diff --git a/indra/cmake/CARes.cmake b/indra/cmake/CARes.cmake index d18e1ef1e..b08f051c8 100644 --- a/indra/cmake/CARes.cmake +++ b/indra/cmake/CARes.cmake @@ -13,12 +13,15 @@ else (STANDALONE) add_definitions("-DCARES_STATICLIB") set(CARES_LIBRARIES areslib) elseif (DARWIN) - set(CARES_LIBRARIES - optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libcares.a - debug ${ARCH_PREBUILT_DIRS_DEBUG}/libcares.a + set(APR_LIBRARIES + debug libcares.a + optimized libcares.a ) else (WINDOWS) set(CARES_LIBRARIES cares) endif (WINDOWS) - set(CARES_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/ares) + set(CARES_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include/ares + ${LIBS_PREBUILT_LEGACY_DIR}/include/ares + ) endif (STANDALONE) diff --git a/indra/cmake/CURL.cmake b/indra/cmake/CURL.cmake index 10ef1b0b6..8463d6531 100644 --- a/indra/cmake/CURL.cmake +++ b/indra/cmake/CURL.cmake @@ -18,5 +18,8 @@ else (STANDALONE) list(APPEND CURL_LIBRARIES idn) endif(LINUX AND WORD_SIZE EQUAL 64) endif (WINDOWS) - set(CURL_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + set(CURL_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) endif (STANDALONE) diff --git a/indra/cmake/Colladadom.cmake b/indra/cmake/Colladadom.cmake index cc4fa50ea..afc8bcce0 100644 --- a/indra/cmake/Colladadom.cmake +++ b/indra/cmake/Colladadom.cmake @@ -20,9 +20,11 @@ else (STANDALONE) endif (NOT DARWIN AND NOT WINDOWS) set(COLLADADOM_INCLUDE_DIRS - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/collada - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/collada/1.4 - ) + ${LIBS_PREBUILT_DIR}/include/collada + ${LIBS_PREBUILT_DIR}/include/collada/1.4 + ${LIBS_PREBUILT_LEGACY_DIR}/include/collada + ${LIBS_PREBUILT_LEGACY_DIR}/include/collada/1.4 + ) if (WINDOWS) if(MSVC12) diff --git a/indra/cmake/CopyWinLibs.cmake b/indra/cmake/CopyWinLibs.cmake deleted file mode 100644 index 2e6c1fe05..000000000 --- a/indra/cmake/CopyWinLibs.cmake +++ /dev/null @@ -1,328 +0,0 @@ -# -*- cmake -*- - -# The copy_win_libs folder contains file lists and a script used to -# copy dlls, exes and such needed to run the SecondLife from within -# VisualStudio. - -include(CMakeCopyIfDifferent) - -if(WORD_SIZE EQUAL 32) - set(debug_libs_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/debug") - set(release_libs_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/release") -else(WORD_SIZE EQUAL 32) - set(debug_libs_dir "${CMAKE_SOURCE_DIR}/../libraries/x86_64-win/lib/debug") - set(release_libs_dir "${CMAKE_SOURCE_DIR}/../libraries/x86_64-win/lib/release") -endif(WORD_SIZE EQUAL 32) - -set(vivox_src_dir "${CMAKE_SOURCE_DIR}/newview/vivox-runtime/i686-win32") -set(vivox_files - ca-bundle.crt - libsndfile-1.dll - ortp.dll - SLVoice.exe - vivoxoal.dll - vivoxplatform.dll - vivoxsdk.dll - zlib1.dll - ) -copy_if_different( - ${vivox_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/Debug" - out_targets - ${vivox_files} - ) -set(all_targets ${all_targets} ${out_targets}) - - -set(debug_src_dir "${debug_libs_dir}") -set(debug_files - libhunspell.dll - libapr-1.dll - libaprutil-1.dll - libapriconv-1.dll - libeay32.dll - ssleay32.dll - glod.dll - ) - if(MSVC10) - set(release_files ${release_files} - libcollada14dom22-d.dll - ) - endif(MSVC10) - -copy_if_different( - ${debug_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/Debug" - out_targets - ${debug_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -# Debug config runtime files required for the plugin test mule -set(plugintest_debug_src_dir "${debug_libs_dir}") -set(plugintest_debug_files - libeay32.dll - qtcored4.dll - qtguid4.dll - qtnetworkd4.dll - qtopengld4.dll - qtwebkitd4.dll - ssleay32.dll - ) -copy_if_different( - ${plugintest_debug_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/../test_apps/llplugintest/Debug" - out_targets - ${plugintest_debug_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -# Debug config runtime files required for the plugin test mule (Qt image format plugins) -set(plugintest_debug_src_dir "${debug_libs_dir}/imageformats") -set(plugintest_debug_files - qgifd4.dll - qicod4.dll - qjpegd4.dll - qmngd4.dll - qsvgd4.dll - qtiffd4.dll - ) -copy_if_different( - ${plugintest_debug_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/../test_apps/llplugintest/Debug/imageformats" - out_targets - ${plugintest_debug_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -copy_if_different( - ${plugintest_debug_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/llplugin/imageformats" - out_targets - ${plugintest_debug_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -# Release & ReleaseDebInfo config runtime files required for the plugin test mule -set(plugintest_release_src_dir "${release_libs_dir}") -set(plugintest_release_files - libeay32.dll - qtcore4.dll - qtgui4.dll - qtnetwork4.dll - qtopengl4.dll - qtwebkit4.dll - ssleay32.dll - ) -copy_if_different( - ${plugintest_release_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/../test_apps/llplugintest/Release" - out_targets - ${plugintest_release_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -copy_if_different( - ${plugintest_release_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/../test_apps/llplugintest/RelWithDebInfo" - out_targets - ${plugintest_release_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -# Release & ReleaseDebInfo config runtime files required for the plugin test mule (Qt image format plugins) -set(plugintest_release_src_dir "${release_libs_dir}/imageformats") -set(plugintest_release_files - qgif4.dll - qico4.dll - qjpeg4.dll - qmng4.dll - qsvg4.dll - qtiff4.dll - ) -copy_if_different( - ${plugintest_release_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/../test_apps/llplugintest/Release/imageformats" - out_targets - ${plugintest_release_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -copy_if_different( - ${plugintest_release_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/../test_apps/llplugintest/RelWithDebInfo/imageformats" - out_targets - ${plugintest_release_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -copy_if_different( - ${plugintest_release_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/Release/llplugin/imageformats" - out_targets - ${plugintest_release_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -copy_if_different( - ${plugintest_release_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo/llplugin/imageformats" - out_targets - ${plugintest_release_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -# Debug config runtime files required for the plugins -set(plugins_debug_src_dir "${debug_libs_dir}") -set(plugins_debug_files - libeay32.dll - qtcored4.dll - qtguid4.dll - qtnetworkd4.dll - qtopengld4.dll - qtwebkitd4.dll - ssleay32.dll - ) -copy_if_different( - ${plugins_debug_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/Debug/llplugin" - out_targets - ${plugins_debug_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -# Release & ReleaseDebInfo config runtime files required for the plugins -set(plugins_release_src_dir "${release_libs_dir}") -set(plugins_release_files - libeay32.dll - qtcore4.dll - qtgui4.dll - qtnetwork4.dll - qtopengl4.dll - qtwebkit4.dll - ssleay32.dll - ) -copy_if_different( - ${plugins_release_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/Release/llplugin" - out_targets - ${plugins_release_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -copy_if_different( - ${plugins_release_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo/llplugin" - out_targets - ${plugins_release_files} - ) -set(all_targets ${all_targets} ${out_targets}) - - -set(release_src_dir "${release_libs_dir}") -set(release_files - libhunspell.dll - libapr-1.dll - libaprutil-1.dll - libapriconv-1.dll - libeay32.dll - ssleay32.dll - glod.dll - ) - -if(WORD_SIZE EQUAL 32) - set(release_files ${release_files} - libcollada14dom22.dll - libtcmalloc_minimal.dll - ) -endif(WORD_SIZE EQUAL 32) - -if(FMODSTUDIO) - if (WORD_SIZE EQUAL 32) - set(fmodstudio_dll_file "fmod.dll") - else (WORD_SIZE EQUAL 32) - set(fmodstudio_dll_file "fmod64.dll") - endif (WORD_SIZE EQUAL 32) - - find_path(FMODSTUDIO_BINARY_DIR "${fmodstudio_dll_file}" - "${release_src_dir}" - "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib" - "${FMODSTUDIO_SDK_DIR}" - NO_DEFAULT_PATH - ) - - if(FMODSTUDIO_BINARY_DIR) - copy_if_different("${FMODSTUDIO_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Release" out_targets "${fmodstudio_dll_file}") - set(all_targets ${all_targets} ${out_targets}) - copy_if_different("${FMODSTUDIO_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" out_targets "${fmodstudio_dll_file}") - set(all_targets ${all_targets} ${out_targets}) - copy_if_different("${FMODSTUDIO_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Debug" out_targets "${fmodstudio_dll_file}") - set(all_targets ${all_targets} ${out_targets}) - endif(FMODSTUDIO_BINARY_DIR) -endif(FMODSTUDIO) - -if(FMODEX) - if (WORD_SIZE EQUAL 32) - set(fmodex_dll_file "fmodex.dll") - else (WORD_SIZE EQUAL 32) - set(fmodex_dll_file "fmodex64.dll") - endif (WORD_SIZE EQUAL 32) - - find_path(FMODEX_BINARY_DIR "${fmodex_dll_file}" - "${release_src_dir}" - "${FMODEX_SDK_DIR}/api" - "${FMODEX_SDK_DIR}" - NO_DEFAULT_PATH - ) - - if(FMODEX_BINARY_DIR) - copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Release" out_targets "${fmodex_dll_file}") - set(all_targets ${all_targets} ${out_targets}) - copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" out_targets "${fmodex_dll_file}") - set(all_targets ${all_targets} ${out_targets}) - copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Debug" out_targets "${fmodex_dll_file}") - set(all_targets ${all_targets} ${out_targets}) - endif(FMODEX_BINARY_DIR) -endif(FMODEX) - -copy_if_different( - ${release_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/Release" - out_targets - ${release_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -copy_if_different( - ${vivox_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/Release" - out_targets - ${vivox_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -copy_if_different( - ${release_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" - out_targets - ${release_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -copy_if_different( - ${vivox_src_dir} - "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" - out_targets - ${vivox_files} - ) -set(all_targets ${all_targets} ${out_targets}) - -add_custom_target(copy_win_libs ALL - DEPENDS - ${all_targets} - ${release_appconfig_file} - ${relwithdebinfo_appconfig_file} - ${debug_appconfig_file} - ) -add_dependencies(copy_win_libs prepare) - diff --git a/indra/cmake/CopyWinLibs.cmake.in b/indra/cmake/CopyWinLibs.cmake.in new file mode 100644 index 000000000..3b16a0bc7 --- /dev/null +++ b/indra/cmake/CopyWinLibs.cmake.in @@ -0,0 +1,238 @@ +# -*- cmake -*- + +# The copy_win_libs folder contains file lists and a script used to +# copy dlls, exes and such needed to run the SecondLife from within +# VisualStudio. + +set(LIBS_PREBUILT_DIR "@LIBS_PREBUILT_DIR@") +set(LIBS_PREBUILT_LEGACY_DIR "@LIBS_PREBUILT_LEGACY_DIR@") +set(FMODSTUDIO_SDK_DIR "@FMODSTUDIO_SDK_DIR@") +set(FMODEX_SDK_DIR "@FMODEX_SDK_DIR@") +set(FMODSTUDIO @FMODSTUDIO@) +set(FMODEX @FMODEX@) +set(MSVC10 "@MSVC10@") +set(WORD_SIZE "@WORD_SIZE@") + +set(LIBS_RELEASE_DIR + ${LIBS_PREBUILT_DIR}/lib/release + ${LIBS_PREBUILT_LEGACY_DIR}/lib/release + ) +set(LIBS_DEBUG_DIR + ${LIBS_PREBUILT_DIR}/lib/debug + ${LIBS_PREBUILT_LEGACY_DIR}/lib/debug + ) + +function(copy_files paths names dest) + foreach(f ${names}) + foreach(p ${paths}) + set(from_file "${p}/${f}") + set(to_dest "${CMAKE_BINARY_DIR}/newview/${dest}") + if(EXISTS ${from_file}) + message("Copying ${from_file} to ${to_dest}") + if(NOT EXISTS ${to_dest}) + execute_process(COMMAND mkdir -p "${to_dest}") + endif(NOT EXISTS ${to_dest}) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${from_file} ${to_dest} RESULT_VARIABLE result) + if(${result}) + message(SEND_ERROR "Unsuccessful.") + endif(${result}) + set(found 1) + break() + endif(EXISTS ${from_file}) + endforeach(p) + if(NOT found) + message(SEND_ERROR "Failed to find library: ${f}") + endif(NOT found) + endforeach(f) +endfunction(copy_files) + +set(vivox_files + ca-bundle.crt + libsndfile-1.dll + ortp.dll + SLVoice.exe + vivoxoal.dll + vivoxplatform.dll + vivoxsdk.dll + zlib1.dll + ) +copy_files("${LIBS_RELEASE_DIR}" "${vivox_files}" "Release" ) +copy_files("${LIBS_RELEASE_DIR}" "${vivox_files}" "RelWithDebInfo") +copy_files("${LIBS_DEBUG_DIR}" "${vivox_files}" "Debug") + +set(release_files + libhunspell.dll + libapr-1.dll + libaprutil-1.dll + libapriconv-1.dll + libeay32.dll + ssleay32.dll + glod.dll + qtcore4.dll + ) +copy_files("${LIBS_RELEASE_DIR}" "${release_files}" "Release") +copy_files("${LIBS_RELEASE_DIR}" "${release_files}" "RelWithDebInfo") +copy_files("${LIBS_DEBUG_DIR}" "${release_files}" "Debug") + +if(MSVC10) + copy_files("${LIBS_DEBUG_DIR}" "libcollada14dom22-d.dll" "Debug") +endif(MSVC10) + +if(WORD_SIZE EQUAL 32) +set(release_files + libcollada14dom22.dll + libtcmalloc_minimal.dll + ) + copy_files("${LIBS_RELEASE_DIR}" "${release_files}" "Release") + copy_files("${LIBS_RELEASE_DIR}" "${release_files}" "RelWithDebInfo") +endif(WORD_SIZE EQUAL 32) + +set(plugins_release_files + libeay32.dll + qtcore4.dll + qtgui4.dll + qtnetwork4.dll + qtopengl4.dll + qtwebkit4.dll + ssleay32.dll + qtxmlpatterns4.dll + ) +copy_files("${LIBS_RELEASE_DIR}" "${plugins_release_files}" "Release/llplugin") +copy_files("${LIBS_RELEASE_DIR}" "${plugins_release_files}" "RelWithDebInfo/llplugin") +if(0) + copy_files("${LIBS_RELEASE_DIR}" "${plugin_release_files}" "../test_apps/llplugintest/Release") + copy_files("${LIBS_RELEASE_DIR}" "${plugin_release_files}" "../test_apps/llplugintest/RelWithDebInfo") +endif(0) + +# Debug config runtime files required for the plugins +set(plugins_debug_files + libeay32.dll + qtcored4.dll + qtguid4.dll + qtnetworkd4.dll + qtopengld4.dll + qtwebkitd4.dll + ssleay32.dll + qtxmlpatternsd4.dll + ) +copy_files("${LIBS_DEBUG_DIR}" "${plugins_debug_files}" "Debug/llplugin") +if(0) + copy_files("${LIBS_DEBUG_DIR}" "${plugins_debug_files}" "../test_apps/llplugintest/Debug") +endif(0) + +# Release & ReleaseDebInfo config runtime files required for the plugin test mule (Qt image format plugins) +set(plugin_image_release_files + qgif4.dll + qico4.dll + qjpeg4.dll + qmng4.dll + qsvg4.dll + qtiff4.dll + ) +copy_files("${LIBS_RELEASE_DIR}/imageformats" "${plugin_image_release_files}" "Release/llplugin/imageformats") +copy_files("${LIBS_RELEASE_DIR}/imageformats" "${plugin_image_release_files}" "RelWithDebInfo/llplugin/imageformats") +if(0) + copy_files("${LIBS_RELEASE_DIR}/imageformats" "${plugin_image_release_files}" "../test_apps/llplugintest/imageformats/Release") + copy_files("${LIBS_RELEASE_DIR}/imageformats" "${plugin_image_release_files}" "../test_apps/llplugintest/imageformats/RelWithDebInfo") +endif(0) + + +# Debug config runtime files required for the plugin test mule (Qt image format plugins) +set(plugin_image_debug_files + qgifd4.dll + qicod4.dll + qjpegd4.dll + qmngd4.dll + qsvgd4.dll + qtiffd4.dll + ) +copy_files("${LIBS_DEBUG_DIR}/imageformats" "${plugin_image_debug_files}" "Debug/llplugin/imageformats") +if(0) + copy_files("${LIBS_DEBUG_DIR}/imageformats" "${plugin_image_debug_files}" "../test_apps/llplugintest/imageformats/Debug") +endif(0) + +# Release & ReleaseDebInfo config runtime files required for the plugin test mule (Qt image format plugins) +set(plugin_codec_release_files + qcncodecs4.dll + qjpcodecs4.dll + qkrcodecs4.dll + qtwcodecs4.dll + ) +copy_files("${LIBS_RELEASE_DIR}/codecs" "${plugin_codec_release_files}" "Release/llplugin/codecs") +copy_files("${LIBS_RELEASE_DIR}/codecs" "${plugin_codec_release_files}" "RelWithDebInfo/llplugin/codecs") +if(0) + copy_files("${LIBS_RELEASE_DIR}/codecs" "${plugin_codec_release_files}" "../test_apps/llplugintest/codecs/Release") + copy_files("${LIBS_RELEASE_DIR}/codecs" "${plugin_codec_release_files}" "../test_apps/llplugintest/codecs/RelWithDebInfo") +endif(0) + +# Debug config runtime files required for the plugin test mule (Qt image format plugins) +set(plugin_codec_debug_files + qcncodecsd4.dll + qjpcodecsd4.dll + qkrcodecsd4.dll + qtwcodecsd4.dll + ) +copy_files("${LIBS_DEBUG_DIR}/codecs" "${plugin_codec_debug_files}" "Debug/llplugin/codecs") +if(0) + copy_files("${LIBS_DEBUG_DIR}/codecs" "${plugin_codec_debug_files}" "../test_apps/llplugintest/codecs/Debug") +endif(0) + +if(FMODSTUDIO) + if (WORD_SIZE EQUAL 32) + set(fmodstudio_dll_file "fmod.dll") + else (WORD_SIZE EQUAL 32) + set(fmodstudio_dll_file "fmod64.dll") + endif (WORD_SIZE EQUAL 32) + + find_path(FMODSTUDIO_BINARY_RELEASE_DIR "${fmodstudio_dll_file}" + ${LIBS_RELEASE_DIR} + "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib" + "${FMODSTUDIO_SDK_DIR}" + NO_DEFAULT_PATH + ) + find_path(FMODSTUDIO_BINARY_DEBUG_DIR "${fmodstudio_dll_file}" + ${LIBS_DEBUG_DIR} + "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib" + "${FMODSTUDIO_SDK_DIR}" + NO_DEFAULT_PATH + ) + + if(FMODSTUDIO_BINARY_RELEASE_DIR) + copy_files(${FMODSTUDIO_BINARY_RELEASE_DIR} ${fmodstudio_dll_file} "Release") + copy_files(${FMODSTUDIO_BINARY_RELEASE_DIR} ${fmodstudio_dll_file} "RelWithDebInfo") + endif(FMODSTUDIO_BINARY_RELEASE_DIR) + if(FMODSTUDIO_BINARY_DEBUG_DIR) + copy_files(${FMODSTUDIO_BINARY_DEBUG_DIR} ${fmodstudio_dll_file} "Debug") + endif(FMODSTUDIO_BINARY_DEBUG_DIR) +endif(FMODSTUDIO) + +if(FMODEX) + if (WORD_SIZE EQUAL 32) + set(fmodex_dll_file "fmodex.dll") + else (WORD_SIZE EQUAL 32) + set(fmodex_dll_file "fmodex64.dll") + endif (WORD_SIZE EQUAL 32) + + find_path(FMODEX_BINARY_RELEASE_DIR "${fmodex_dll_file}" + ${LIBS_RELEASE_DIR} + "${FMODEX_SDK_DIR}/api" + "${FMODEX_SDK_DIR}" + NO_DEFAULT_PATH + ) + find_path(FMODEX_BINARY_DEBUG_DIR "${fmodex_dll_file}" + ${LIBS_DEBUG_DIR} + "${FMODSTUDIO_SDK_DIR}/api" + "${FMODSTUDIO_SDK_DIR}" + NO_DEFAULT_PATH + ) + + if(FMODEX_BINARY_RELEASE_DIR) + copy_files(${FMODEX_BINARY_RELEASE_DIR ${fmodex_dll_file} "Release") + copy_files(${FMODEX_BINARY_RELEASE_DIR ${fmodex_dll_file} "RelWithDebInfo") + endif(FMODEX_BINARY_RELEASE_DIR) + if(FMODEX_BINARY_DEBUG_DIR) + copy_files(${FMODEX_BINARY_DEBUG_DIR ${fmodex_dll_file} "Debug") + endif(FMODEX_BINARY_DEBUG_DIR) +endif(FMODEX) + + diff --git a/indra/cmake/DBusGlib.cmake b/indra/cmake/DBusGlib.cmake index 05266eb9c..cb1b3a302 100644 --- a/indra/cmake/DBusGlib.cmake +++ b/indra/cmake/DBusGlib.cmake @@ -10,8 +10,9 @@ elseif (LINUX) use_prebuilt_binary(dbusglib) set(DBUSGLIB_FOUND ON FORCE BOOL) set(DBUSGLIB_INCLUDE_DIRS - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/dbus - ) + ${LIBS_PREBUILT_DIR}/include/dbus + ${LIBS_PREBUILT_LEGACY_DIR}/include/dbus + ) # We don't need to explicitly link against dbus-glib itself, because # the viewer probes for the system's copy at runtime. set(DBUSGLIB_LIBRARIES diff --git a/indra/cmake/DownloadPrebuilt.cmake.in b/indra/cmake/DownloadPrebuilt.cmake.in index 7e4071d8a..5b67fedd4 100644 --- a/indra/cmake/DownloadPrebuilt.cmake.in +++ b/indra/cmake/DownloadPrebuilt.cmake.in @@ -2,7 +2,7 @@ # The top-level CMakeLists.txt configures packages and tool locations. set(packages "@PREBUILT_PACKAGES@") set(python "@PYTHON_EXECUTABLE@") -set(install_dir "@CMAKE_SOURCE_DIR@/..") +set(install_dir "@CMAKE_BINARY_DIR@/packages") set(scp "@SCP_EXECUTABLE@") set(scripts_dir "@SCRIPTS_DIR@") set(sentinel_dir "@CMAKE_BINARY_DIR@/prepare") @@ -20,7 +20,7 @@ foreach(package ${packages}) # This package is missing or out of date. message(STATUS "Obtaining${proprietary_message} prebuilt '${package}'") execute_process( - COMMAND ${python} install.py -p${prebuilt_type} --install-dir=${install_dir} ${scp_option} ${package} + COMMAND ${python} install.py -p${prebuilt_type} --install-dir=${install_dir} --installed-manifest=${install_dir}/installed.xml ${scp_option} ${package} WORKING_DIRECTORY ${scripts_dir} RESULT_VARIABLE result ) diff --git a/indra/cmake/ELFIO.cmake b/indra/cmake/ELFIO.cmake index fbde78311..8de2c36d4 100644 --- a/indra/cmake/ELFIO.cmake +++ b/indra/cmake/ELFIO.cmake @@ -8,7 +8,10 @@ if (STANDALONE) elseif (LINUX) use_prebuilt_binary(elfio) set(ELFIO_LIBRARIES ELFIO) - set(ELFIO_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + set(ELFIO_INCLUDE_DIR + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) set(ELFIO_FOUND "YES") endif (STANDALONE) diff --git a/indra/cmake/EXPAT.cmake b/indra/cmake/EXPAT.cmake index 895e15f0a..bafe1a91e 100644 --- a/indra/cmake/EXPAT.cmake +++ b/indra/cmake/EXPAT.cmake @@ -17,5 +17,8 @@ else (STANDALONE) else (WINDOWS) set(EXPAT_LIBRARIES expat) endif (WINDOWS) - set(EXPAT_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + set(EXPAT_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) endif (STANDALONE) diff --git a/indra/cmake/FMODEX.cmake b/indra/cmake/FMODEX.cmake index 7a7bdc5ac..acd419e9e 100644 --- a/indra/cmake/FMODEX.cmake +++ b/indra/cmake/FMODEX.cmake @@ -57,8 +57,8 @@ if (NOT FMODEX_LIBRARY) endif (NOT FMODEX_LIBRARY) find_path(FMODEX_INCLUDE_DIR fmod.hpp - "${LIBS_PREBUILT_DIR}/include/fmodex" - "${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/fmodex" + ${LIBS_PREBUILT_DIR}/include/fmodex + ${LIBS_PREBUILT_LEGACY_DIR}/include/fmodex "${FMODEX_SDK_DIR}/api/inc" "${FMODEX_SDK_DIR}/inc" "${FMODEX_SDK_DIR}" diff --git a/indra/cmake/FMODSTUDIO.cmake b/indra/cmake/FMODSTUDIO.cmake index b2afae207..ef8f65253 100644 --- a/indra/cmake/FMODSTUDIO.cmake +++ b/indra/cmake/FMODSTUDIO.cmake @@ -55,8 +55,8 @@ if (NOT FMODSTUDIO_LIBRARY) endif (NOT FMODSTUDIO_LIBRARY) find_path(FMODSTUDIO_INCLUDE_DIR fmod.hpp - "${LIBS_PREBUILT_DIR}/include/fmodstudio" - "${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/fmodstudio" + ${LIBS_PREBUILT_DIR}/include/fmodstudio + ${LIBS_PREBUILT_LEGACY_DIR}/include/fmodstudio "${FMODSTUDIO_SDK_DIR}/api/lowlevel/inc" "${FMODSTUDIO_SDK_DIR}" ) diff --git a/indra/cmake/FreeType.cmake b/indra/cmake/FreeType.cmake index 7ede1c749..5cca587cf 100644 --- a/indra/cmake/FreeType.cmake +++ b/indra/cmake/FreeType.cmake @@ -7,16 +7,17 @@ if (STANDALONE) pkg_check_modules(FREETYPE REQUIRED freetype2) else (STANDALONE) use_prebuilt_binary(freetype) - if (LINUX) + if(MSVC12) set(FREETYPE_INCLUDE_DIRS - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) - else (LINUX) - if(MSVC12) - set(FREETYPE_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/freetype2) - else(MSVC12) - set(FREETYPE_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) - endif (MSVC12) - endif (LINUX) + ${LIBS_PREBUILT_DIR}/include/freetype2 + ${LIBS_PREBUILT_LEGACY_DIR}/include/freetype2 + ) + else(MSVC12) + set(FREETYPE_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) + endif (MSVC12) set(FREETYPE_LIBRARIES freetype) endif (STANDALONE) diff --git a/indra/cmake/GLOD.cmake b/indra/cmake/GLOD.cmake index e46a72c86..d688db2fe 100644 --- a/indra/cmake/GLOD.cmake +++ b/indra/cmake/GLOD.cmake @@ -8,6 +8,9 @@ if (STANDALONE) include(FindGLOD) else (STANDALONE) use_prebuilt_binary(GLOD) - set(GLOD_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + set(GLOD_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) set(GLOD_LIBRARIES glod) endif (STANDALONE) diff --git a/indra/cmake/GStreamer010Plugin.cmake b/indra/cmake/GStreamer010Plugin.cmake index 90ed35c81..9fdf60eba 100644 --- a/indra/cmake/GStreamer010Plugin.cmake +++ b/indra/cmake/GStreamer010Plugin.cmake @@ -16,10 +16,13 @@ else (STANDALONE) set(GSTREAMER010_FOUND ON FORCE BOOL) set(GSTREAMER010_PLUGINS_BASE_FOUND ON FORCE BOOL) set(GSTREAMER010_INCLUDE_DIRS - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/gstreamer-0.10 - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/glib-2.0 - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/libxml2 - ) + ${LIBS_PREBUILT_DIR}/include/gstreamer-0.10 + ${LIBS_PREBUILT_DIR}/includeg/lib-2.0 + ${LIBS_PREBUILT_DIR}/include/libxml2 + ${LIBS_PREBUILT_LEGACY_DIR}/include/gstreamer-0.10 + ${LIBS_PREBUILT_LEGACY_DIR}/include/glib-2.0 + ${LIBS_PREBUILT_LEGACY_DIR}/include/libxml2 + ) endif (STANDALONE) diff --git a/indra/cmake/GoogleBreakpad.cmake b/indra/cmake/GoogleBreakpad.cmake index 69e54ee1b..de899a462 100644 --- a/indra/cmake/GoogleBreakpad.cmake +++ b/indra/cmake/GoogleBreakpad.cmake @@ -17,5 +17,10 @@ else (STANDALONE) endif (WINDOWS) # yes, this does look dumb, no, it's not incorrect # I think it's incorrect: the second one should go --Aleric - set(BREAKPAD_INCLUDE_DIRECTORIES "${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/google_breakpad" "${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/google_breakpad/google_breakpad") + set(BREAKPAD_INCLUDE_DIRECTORIES + ${LIBS_PREBUILT_DIR}/include/google_breakpad + ${LIBS_PREBUILT_LEGACY_DIR}/include/google_breakpad + ${LIBS_PREBUILT_DIR}/include/google_breakpad/google_breakpad + ${LIBS_PREBUILT_LEGACY_DIR}/include/google_breakpad/google_breakpad + ) endif (STANDALONE) diff --git a/indra/cmake/GooglePerfTools.cmake b/indra/cmake/GooglePerfTools.cmake index d92c39ded..49ff3f5eb 100644 --- a/indra/cmake/GooglePerfTools.cmake +++ b/indra/cmake/GooglePerfTools.cmake @@ -25,7 +25,9 @@ else (STANDALONE) set(TCMALLOC_LIBRARIES tcmalloc_minimal) endif() set(GOOGLE_PERFTOOLS_INCLUDE_DIR - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) endif (LINUX) endif (STANDALONE) diff --git a/indra/cmake/Hunspell.cmake b/indra/cmake/Hunspell.cmake index 363f061b5..3ea97afdf 100644 --- a/indra/cmake/Hunspell.cmake +++ b/indra/cmake/Hunspell.cmake @@ -6,7 +6,10 @@ if (STANDALONE) else (STANDALONE) use_prebuilt_binary(hunspell) - set(HUNSPELL_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/hunspell) + set(HUNSPELL_INCLUDE_DIR + ${LIBS_PREBUILT_DIR}/include/hunspell + ${LIBS_PREBUILT_LEGACY_DIR}/include/hunspell + ) if (LINUX OR DARWIN) set(HUNSPELL_LIBRARY hunspell-1.3) diff --git a/indra/cmake/JPEG.cmake b/indra/cmake/JPEG.cmake index 5c55aedbb..2fe1beb51 100644 --- a/indra/cmake/JPEG.cmake +++ b/indra/cmake/JPEG.cmake @@ -13,11 +13,14 @@ else (STANDALONE) set(JPEG_LIBRARIES jpeg) elseif (DARWIN) set(JPEG_LIBRARIES - optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libjpeg.a - debug ${ARCH_PREBUILT_DIRS_DEBUG}/libjpeg.a + debug libjpeg.a + optimized libjpeg.a ) elseif (WINDOWS) set(JPEG_LIBRARIES jpeglib) endif (LINUX) - set(JPEG_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + set(JPEG_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) endif (STANDALONE) diff --git a/indra/cmake/JsonCpp.cmake b/indra/cmake/JsonCpp.cmake index f088087cf..3ab859d4d 100644 --- a/indra/cmake/JsonCpp.cmake +++ b/indra/cmake/JsonCpp.cmake @@ -24,5 +24,8 @@ else (STANDALONE) elseif (LINUX) set(JSONCPP_LIBRARIES jsoncpp) endif (WINDOWS) - set(JSONCPP_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/jsoncpp) + set(JSONCPP_INCLUDE_DIR + ${LIBS_PREBUILT_DIR}/include/jsoncpp + ${LIBS_PREBUILT_LEGACY_DIR}/include/jsoncpp + ) endif (STANDALONE) diff --git a/indra/cmake/LLWindow.cmake b/indra/cmake/LLWindow.cmake index a46d9c592..c5de9d269 100644 --- a/indra/cmake/LLWindow.cmake +++ b/indra/cmake/LLWindow.cmake @@ -17,7 +17,10 @@ else (STANDALONE) use_prebuilt_binary(mesa) use_prebuilt_binary(SDL) set (SDL_FOUND TRUE) - set (SDL_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}) + set (SDL_INCLUDE_DIR + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) if(WORD_SIZE EQUAL 64) set (SDL_LIBRARY SDL) else(WORD_SIZE EQUAL 64) diff --git a/indra/cmake/Linking.cmake b/indra/cmake/Linking.cmake index e7bc897c9..36f087545 100644 --- a/indra/cmake/Linking.cmake +++ b/indra/cmake/Linking.cmake @@ -5,9 +5,18 @@ set(${CMAKE_CURRENT_LIST_FILE}_INCLUDED "YES") include(Variables) if (NOT STANDALONE) - set(ARCH_PREBUILT_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/lib) - set(ARCH_PREBUILT_DIRS_RELEASE ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/lib/release) - set(ARCH_PREBUILT_DIRS_DEBUG ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/lib/debug) + set(ARCH_PREBUILT_DIRS + ${LIBS_PREBUILT_DIR}/lib + ${LIBS_PREBUILT_LEGACY_DIR}/lib + ) + set(ARCH_PREBUILT_DIRS_RELEASE + ${LIBS_PREBUILT_DIR}/lib/release + ${LIBS_PREBUILT_LEGACY_DIR}/lib/release + ) + set(ARCH_PREBUILT_DIRS_DEBUG + ${LIBS_PREBUILT_DIR}/lib/debug + ${LIBS_PREBUILT_LEGACY_DIR}/lib/debug + ) if(WINDOWS OR ${CMAKE_GENERATOR} MATCHES "Xcode") # the cmake xcode and VS generators implicitly append ${CMAKE_CFG_INTDIR} to the library paths for us @@ -37,7 +46,7 @@ else (LINUX) set(PTHREAD_LIBRARY "") endif (LINUX) -if (WINDOWS) +if (WINDOWS) set(WINDOWS_LIBRARIES advapi32 shell32 diff --git a/indra/cmake/NDOF.cmake b/indra/cmake/NDOF.cmake index a4e210405..d7fb4e686 100644 --- a/indra/cmake/NDOF.cmake +++ b/indra/cmake/NDOF.cmake @@ -15,7 +15,10 @@ else (STANDALONE) set(NDOF_LIBRARY ndofdev) endif (WINDOWS) - set(NDOF_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/ndofdev) + set(NDOF_INCLUDE_DIR + ${LIBS_PREBUILT_DIR}/include/ndofdev + ${LIBS_PREBUILT_LEGACY_DIR}/include/ndofdev + ) set(NDOF_FOUND 1) endif (STANDALONE) diff --git a/indra/cmake/OPENAL.cmake b/indra/cmake/OPENAL.cmake index d5ab802d9..c53d2e710 100644 --- a/indra/cmake/OPENAL.cmake +++ b/indra/cmake/OPENAL.cmake @@ -21,7 +21,10 @@ if (OPENAL) openal alut ) - set(OPENAL_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + set(OPENAL_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) endif (OPENAL) if (OPENAL) diff --git a/indra/cmake/OpenGL.cmake b/indra/cmake/OpenGL.cmake index 0bfc1a359..abf908f23 100644 --- a/indra/cmake/OpenGL.cmake +++ b/indra/cmake/OpenGL.cmake @@ -3,5 +3,8 @@ include(Prebuilt) if (NOT (STANDALONE OR DARWIN)) use_prebuilt_binary(glext) - set(GLEXT_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + set(GLEXT_INCLUDE_DIR + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) endif (NOT (STANDALONE OR DARWIN)) diff --git a/indra/cmake/OpenSSL.cmake b/indra/cmake/OpenSSL.cmake index 51532054c..d363e6af3 100644 --- a/indra/cmake/OpenSSL.cmake +++ b/indra/cmake/OpenSSL.cmake @@ -13,7 +13,10 @@ else (STANDALONE OR USE_SYSTEM_OPENSSL) else (WINDOWS) set(OPENSSL_LIBRARIES ssl) endif (WINDOWS) - set(OPENSSL_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + set(OPENSSL_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) endif (STANDALONE OR USE_SYSTEM_OPENSSL) if (LINUX) diff --git a/indra/cmake/PNG.cmake b/indra/cmake/PNG.cmake index 70739ad46..df1d11143 100644 --- a/indra/cmake/PNG.cmake +++ b/indra/cmake/PNG.cmake @@ -34,8 +34,14 @@ else (STANDALONE) # set(PNG_PRELOAD_ARCHIVES -Wl,--whole-archive png16 -Wl,--no-whole-archive) set(PNG_LIBRARIES png16) - set(PNG_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/libpng16) + set(PNG_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include/libpng16 + ${LIBS_PREBUILT_LEGACY_DIR}/include/libpng16 + ) endif () endif() - set(PNG_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/) + set(PNG_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) endif (STANDALONE) diff --git a/indra/cmake/PulseAudio.cmake b/indra/cmake/PulseAudio.cmake index dafdd4163..62e2bf9b2 100644 --- a/indra/cmake/PulseAudio.cmake +++ b/indra/cmake/PulseAudio.cmake @@ -13,8 +13,9 @@ if (PULSEAUDIO) use_prebuilt_binary(pulseaudio) set(PULSEAUDIO_FOUND ON FORCE BOOL) set(PULSEAUDIO_INCLUDE_DIRS - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include - ) + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) # We don't need to explicitly link against pulseaudio itself, because # the viewer probes for the system's copy at runtime. set(PULSEAUDIO_LIBRARIES diff --git a/indra/cmake/QuickTimePlugin.cmake b/indra/cmake/QuickTimePlugin.cmake index cb54e287a..d9317a25b 100644 --- a/indra/cmake/QuickTimePlugin.cmake +++ b/indra/cmake/QuickTimePlugin.cmake @@ -14,13 +14,15 @@ elseif (WINDOWS AND WORD_SIZE EQUAL 32) find_library(DEBUG_QUICKTIME_LIBRARY qtmlclient PATHS - ${ARCH_PREBUILT_DIRS_DEBUG} + ${LIBS_PREBUILT_DIR}/lib/debug + ${LIBS_PREBUILT_LEGACY_DIR}/lib/debug "${QUICKTIME_SDK_DIR}\\libraries" ) find_library(RELEASE_QUICKTIME_LIBRARY qtmlclient PATHS - ${ARCH_PREBUILT_DIRS_RELEASE} + ${LIBS_PREBUILT_DIR}/lib/release + ${LIBS_PREBUILT_LEGACY_DIR}/lib/release "${QUICKTIME_SDK_DIR}\\libraries" ) @@ -33,7 +35,8 @@ elseif (WINDOWS AND WORD_SIZE EQUAL 32) endif (DEBUG_QUICKTIME_LIBRARY AND RELEASE_QUICKTIME_LIBRARY) include_directories( - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/quicktime + ${LIBS_PREBUILT_DIR}/include/quicktime + ${LIBS_PREBUILT_LEGACY_DIR}/include/quicktime "${QUICKTIME_SDK_DIR}\\CIncludes" ) endif (DARWIN) diff --git a/indra/cmake/UI.cmake b/indra/cmake/UI.cmake index 679b5bf3a..697ac4288 100644 --- a/indra/cmake/UI.cmake +++ b/indra/cmake/UI.cmake @@ -55,12 +55,18 @@ else (STANDALONE) endif (LINUX) include_directories ( - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/cairo - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/pixman-1 + ${LIBS_PREBUILT_DIR}/packages/include + ${LIBS_PREBUILT_DIR}/packages/include/cairo + ${LIBS_PREBUILT_DIR}/packages/include/pixman-1 + ${LIBS_PREBUILT_LEGACY_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include/cairo + ${LIBS_PREBUILT_LEGACY_DIR}/include/pixman-1 ) foreach(include ${${LL_ARCH}_INCLUDES}) - include_directories(${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/${include}) + include_directories( + ${LIBS_PREBUILT_DIR}/include/${include} + ${LIBS_PREBUILT_LEGACY_DIR}/include/${include} + ) endforeach(include) endif (STANDALONE) diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index a8df1d307..5b9890d5b 100644 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -32,9 +32,6 @@ set(DISABLE_TCMALLOC OFF CACHE BOOL "Disable linkage of TCMalloc. (64bit builds set(LL_TESTS OFF CACHE BOOL "Build and run unit and integration tests (disable for build timing runs to reduce variation)") set(DISABLE_FATAL_WARNINGS TRUE CACHE BOOL "Set this to FALSE to enable fatal warnings.") -set(LIBS_PREBUILT_DIR ${CMAKE_SOURCE_DIR}/../libraries CACHE PATH - "Location of prebuilt libraries.") - if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") set(WINDOWS ON BOOL FORCE) if (WORD_SIZE EQUAL 32) @@ -154,6 +151,12 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(LL_ARCH_DIR universal-darwin) endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") +set(LIBS_PREBUILT_DIR ${CMAKE_BINARY_DIR}/packages CACHE PATH + "Location of prebuilt libraries.") + +set(LIBS_PREBUILT_LEGACY_DIR ${CMAKE_BINARY_DIR}/packages/libraries/${LL_ARCH_DIR} CACHE PATH + "Legacy location of prebuilt libraries.") + if (WINDOWS AND WORD_SIZE EQUAL 32) set(PREBUILT_TYPE windows) elseif (WINDOWS AND WORD_SIZE EQUAL 64) diff --git a/indra/cmake/WebKitLibPlugin.cmake b/indra/cmake/WebKitLibPlugin.cmake index 7a6305f22..8551aa163 100644 --- a/indra/cmake/WebKitLibPlugin.cmake +++ b/indra/cmake/WebKitLibPlugin.cmake @@ -32,8 +32,8 @@ if (WINDOWS) ) elseif (DARWIN) set(WEBKIT_PLUGIN_LIBRARIES - optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libllqtwebkit.dylib - debug ${ARCH_PREBUILT_DIRS_DEBUG}/libllqtwebkit.dylib + debug libllqtwebkit.dylib + optimized libllqtwebkit.dylib ) elseif (LINUX) if (STANDALONE) diff --git a/indra/cmake/XmlRpcEpi.cmake b/indra/cmake/XmlRpcEpi.cmake index cbfe1e16f..2c93fd20c 100644 --- a/indra/cmake/XmlRpcEpi.cmake +++ b/indra/cmake/XmlRpcEpi.cmake @@ -9,5 +9,8 @@ if (STANDALONE) else (STANDALONE) use_prebuilt_binary(xmlrpc-epi) set(XMLRPCEPI_LIBRARIES xmlrpc-epi) - set(XMLRPCEPI_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include) + set(XMLRPCEPI_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include + ${LIBS_PREBUILT_LEGACY_DIR}/include + ) endif (STANDALONE) diff --git a/indra/cmake/ZLIB.cmake b/indra/cmake/ZLIB.cmake index c133248be..3384c55ee 100644 --- a/indra/cmake/ZLIB.cmake +++ b/indra/cmake/ZLIB.cmake @@ -17,6 +17,9 @@ else (STANDALONE) set(ZLIB_LIBRARIES z) endif (WINDOWS) if (WINDOWS OR LINUX) - set(ZLIB_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/zlib) + set(ZLIB_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/include/zlib + ${LIBS_PREBUILT_LEGACY_DIR}/include/zlib + ) endif (WINDOWS OR LINUX) endif (STANDALONE) diff --git a/indra/develop.py b/indra/develop.py index b712f5a7b..28d09674f 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -115,7 +115,7 @@ class PlatformSetup(object): This can return more than one directory, e.g. if doing a 32-bit viewer and server build on Linux.''' - return ['build-' + self.platform()] + return ['../build-' + self.platform()] def cmake_commandline(self, src_dir, build_dir, opts, simple): '''Return the command line to run cmake with.''' @@ -277,7 +277,7 @@ class LinuxSetup(UnixSetup): def build_dirs(self): platform_build = '%s-%s' % (self.platform(), self.build_type.lower()) - return ['viewer-' + platform_build] + return ['../viewer-' + platform_build] def cmake_commandline(self, src_dir, build_dir, opts, simple): args = dict( @@ -502,9 +502,9 @@ class WindowsSetup(PlatformSetup): def build_dirs(self): if self.word_size == 64: - return ['build-' + self.generator + '-Win64'] + return ['../build-' + self.generator + '-Win64'] else: - return ['build-' + self.generator] + return ['../build-' + self.generator] def cmake_commandline(self, src_dir, build_dir, opts, simple): args = dict( diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 2d3eb001b..ce78f0fbd 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -46,7 +46,6 @@ include(GLOD) include(LLAppearance) if (WINDOWS) - include(CopyWinLibs) set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP) include(InstallRequiredSystemLibraries) endif (WINDOWS) @@ -1309,18 +1308,26 @@ if (WINDOWS) if(FMODEX) list(APPEND viewer_LIBRARIES ${FMODEX_LIBRARY}) endif(FMODEX) + find_library(DEBUG_INTEL_MEMOPS_LIBRARY ll_intel_memops + PATHS + ${LIBS_PREBUILT_DIR}/lib/debug + ${LIBS_PREBUILT_LEGACY_DIR}/lib/debug + "${QUICKTIME_SDK_DIR}\\libraries" + ) - find_library(INTEL_MEMOPS_LIBRARY - NAMES ll_intel_memops - PATHS - optimized ${ARCH_PREBUILT_DIRS_RELEASE} - debug ${ARCH_PREBUILT_DIRS_DEBUG} - ) - mark_as_advanced(INTEL_MEMOPS_LIBRARY) + find_library(RELEASE_INTEL_MEMOPS_LIBRARY ll_intel_memops + PATHS + ${LIBS_PREBUILT_DIR}/lib/release + ${LIBS_PREBUILT_LEGACY_DIR}/lib/release + "${QUICKTIME_SDK_DIR}\\libraries" + ) - if (INTEL_MEMOPS_LIBRARY) - list(APPEND viewer_LIBRARIES ${INTEL_MEMOPS_LIBRARY}) - endif (INTEL_MEMOPS_LIBRARY) + if (DEBUG_INTEL_MEMOPS_LIBRARY AND RELEASE_INTEL_MEMOPS_LIBRARY) + list(APPEND viewer_LIBRARIES optimized ${RELEASE_INTEL_MEMOPS_LIBRARY}) + list(APPEND viewer_LIBRARIES debug ${DEBUG_INTEL_MEMOPS_LIBRARY}) + mark_as_advanced(RELEASE_INTEL_MEMOPS_LIBRARY) + mark_as_advanced(DEBUG_INTEL_MEMOPS_LIBRARY) + endif (DEBUG_INTEL_MEMOPS_LIBRARY AND RELEASE_INTEL_MEMOPS_LIBRARY) endif (WINDOWS) # Add the xui files. This is handy for searching for xui elements @@ -1449,9 +1456,9 @@ if (WINDOWS) endif (WORD_SIZE EQUAL 32) if(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${FMODSTUDIO_BINARY_DIR}/${fmodstudio_dll_file}") + set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${fmodstudio_dll_file}") else(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "--extra_libraries=${FMODSTUDIO_BINARY_DIR}/${fmodstudio_dll_file}") + set(MANIFEST_LIBRARIES "--extra_libraries=${fmodstudio_dll_file}") endif(MANIFEST_LIBRARIES) set(EXTRA_LINKER_FLAGS "/DELAYLOAD:${fmodstudio_dll_file}") endif (FMODSTUDIO) @@ -1464,9 +1471,9 @@ if (WINDOWS) endif (WORD_SIZE EQUAL 32) if(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${FMODEX_BINARY_DIR}/${fmodex_dll_file}") + set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${fmodex_dll_file}") else(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "--extra_libraries=${FMODEX_BINARY_DIR}/${fmodex_dll_file}") + set(MANIFEST_LIBRARIES "--extra_libraries=${fmodex_dll_file}") endif(MANIFEST_LIBRARIES) set(EXTRA_LINKER_FLAGS "/DELAYLOAD:${fmodex_dll_file}") endif (FMODEX) @@ -1515,8 +1522,6 @@ if (WINDOWS) COMMENT "Copying message.xml to the runtime folder." ) - add_dependencies(${VIEWER_BINARY_NAME} copy_win_libs) - if (EXISTS ${CMAKE_SOURCE_DIR}/copy_win_scripts) add_dependencies(${VIEWER_BINARY_NAME} copy_win_scripts) endif (EXISTS ${CMAKE_SOURCE_DIR}/copy_win_scripts) @@ -1567,7 +1572,11 @@ if (WINDOWS) COMMENT "Performing viewer_manifest copy" ) - add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_webkit basic_plugin_filepicker winmm_shim) + add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_webkit basic_plugin_filepicker) + + if(WORD_SIZE EQUAL 32) + add_dependencies(${VIEWER_BINARY_NAME} winmm_shim) + endif(WORD_SIZE EQUAL 32) if (PACKAGE) add_custom_target(package_viewer ALL DEPENDS ${CMAKE_CFG_INTDIR}/touched.bat) @@ -1802,7 +1811,7 @@ if (PACKAGE) # set(VIEWER_EXE_GLOBS "${VIEWER_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX} slplugin.exe") set(VIEWER_EXE_GLOBS "${VIEWER_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX}") set(VIEWER_LIB_GLOB "*${CMAKE_SHARED_MODULE_SUFFIX}") - set(VIEWER_COPY_MANIFEST copy_w_viewer_manifest) + set(VIEWER_DUMP_SYMS dump_syms.exe) endif (WINDOWS) if (DARWIN) list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}") @@ -1814,6 +1823,7 @@ if (PACKAGE) set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-darwin.tar.bz2") set(VIEWER_EXE_GLOBS "'${VIEWER_BRANDING_NAME}' SLPlugin") set(VIEWER_LIB_GLOB "*.dylib") + set(VIEWER_DUMP_SYMS dump_syms) endif (DARWIN) if (LINUX) list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/packaged") @@ -1821,6 +1831,7 @@ if (PACKAGE) set(VIEWER_EXE_GLOBS "${VIEWER_BRANDING_ID}-do-not-run-directly SLPlugin") set(VIEWER_LIB_GLOB "*${CMAKE_SHARED_MODULE_SUFFIX}*") set(VIEWER_COPY_MANIFEST copy_l_viewer_manifest) + set(VIEWER_DUMP_SYMS dump_syms) endif (LINUX) if(RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) @@ -1839,13 +1850,16 @@ if (PACKAGE) "${SYMBOL_SEARCH_DIRS}" "${VIEWER_EXE_GLOBS}" "${VIEWER_LIB_GLOB}" - "${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/bin/dump_syms" + "${LIBS_PREBUILT_DIR}/bin/${VIEWER_DUMP_SYMS}|${LIBS_PREBUILT_LEGACY_DIR}/bin/${VIEWER_DUMP_SYMS}" "${VIEWER_SYMBOL_FILE}" DEPENDS generate_breakpad_symbols.py VERBATIM) add_custom_target(generate_breakpad_symbols DEPENDS "${VIEWER_SYMBOL_FILE}") - add_dependencies(generate_breakpad_symbols "${VIEWER_BINARY_NAME}" "${VIEWER_COPY_MANIFEST}") + add_dependencies(generate_breakpad_symbols ${VIEWER_BINARY_NAME}) +if(VIEWER_COPY_MANIFEST) + add_dependencies(generate_breakpad_symbols ${VIEWER_COPY_MANIFEST}) +endif(VIEWER_COPY_MANIFEST) add_dependencies(package_viewer generate_breakpad_symbols) endif(RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) endif (PACKAGE) diff --git a/indra/newview/generate_breakpad_symbols.py b/indra/newview/generate_breakpad_symbols.py index 4181e4ebb..c1fd05935 100644 --- a/indra/newview/generate_breakpad_symbols.py +++ b/indra/newview/generate_breakpad_symbols.py @@ -83,10 +83,15 @@ def main(configuration, search_dirs, viewer_exes, libs_suffix, dump_syms_tool, v def dump_module(m): print "dumping module '%s' with '%s'..." % (m, dump_syms_tool) dsym_full_path = m - child = subprocess.Popen([dump_syms_tool, dsym_full_path] , stdout=subprocess.PIPE) - out, err = child.communicate() - return (m,child.returncode, out, err) - + + tools = dump_syms_tool.split('|') + for tool in tools: + if tool: + if os.path.isfile(tool): + child = subprocess.Popen([tool, dsym_full_path] , stdout=subprocess.PIPE) + out, err = child.communicate() + return (m,child.returncode, out, err) + raise IOError('Failed to find dump_syms tool!') modules = {} diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 6ec0fe196..342318b26 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -187,11 +187,8 @@ class WindowsManifest(ViewerManifest): # the final exe is complicated because we're not sure where it's coming from, # nor do we have a fixed name for the executable self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe()) - - if self.is_win64(): - release_lib_dir = "../../libraries/x86_64-win/lib/release" - else: - release_lib_dir = "../../libraries/i686-win32/lib/release" + + release_lib_dir = "Release" # Plugin host application self.path(os.path.join(os.pardir, @@ -256,7 +253,7 @@ class WindowsManifest(ViewerManifest): self.end_prefix() # For WebKit/Qt plugin runtimes - if self.prefix(src=release_lib_dir, dst="llplugin"): + if self.prefix(src=release_lib_dir+"/llplugin", dst="llplugin"): self.path("libeay32.dll") self.path("qtcore4.dll") self.path("qtgui4.dll") @@ -268,7 +265,7 @@ class WindowsManifest(ViewerManifest): self.end_prefix() # For WebKit/Qt plugin runtimes (image format plugins) - if self.prefix(src=release_lib_dir+"/imageformats", dst="llplugin/imageformats"): + if self.prefix(src=release_lib_dir+"/llplugin/imageformats", dst="llplugin/imageformats"): self.path("qgif4.dll") self.path("qico4.dll") self.path("qjpeg4.dll") @@ -277,7 +274,7 @@ class WindowsManifest(ViewerManifest): self.path("qtiff4.dll") self.end_prefix() - if self.prefix(src=release_lib_dir+"/codecs", dst="llplugin/codecs"): + if self.prefix(src=release_lib_dir+"/llplugin/codecs", dst="llplugin/codecs"): self.path("qcncodecs4.dll") self.path("qjpcodecs4.dll") self.path("qkrcodecs4.dll") @@ -311,7 +308,7 @@ class WindowsManifest(ViewerManifest): self.path("msvc*.dll") except: try: - if self.prefix(release_lib_dir+"/msvcrt", dst=""): + if self.prefix(src=release_lib_dir+"/msvcrt", dst=""): self.path("*.dll") self.path("*.manifest") self.end_prefix() @@ -319,7 +316,7 @@ class WindowsManifest(ViewerManifest): pass # Vivox runtimes - if self.prefix(src="vivox-runtime/i686-win32", dst=""): + if self.prefix(src=release_lib_dir, dst=""): self.path("SLVoice.exe") self.path("ca-bundle.crt") self.path("libsndfile-1.dll") @@ -334,9 +331,8 @@ class WindowsManifest(ViewerManifest): print self.args['extra_libraries'] path_list = self.args['extra_libraries'].split('|') for path in path_list: - path_pair = path.rsplit('/', 1) - if self.prefix(src=path_pair[0], dst=""): - self.path(path_pair[1]) + if self.prefix(src=release_lib_dir, dst=""): + self.path(path) self.end_prefix() self.package_file = 'none' diff --git a/install.xml b/install.xml index 1760d1f4f..7ce1fa21e 100644 --- a/install.xml +++ b/install.xml @@ -650,9 +650,9 @@ windows64 md5sum - 61f46d94a137f8a50bc6328ed8a69762 + 69e360938a6a13d9b10edfe09dc1bd9d url - https://bitbucket.org/SingularityViewer/libraries/downloads/google_breakpad-1351-windows64-vs12-20140723.tar.bz2 + https://bitbucket.org/alchemyviewer/publiclibs-vs12/downloads/google_breakpad-1400-windows64-20141105.tar.bz2 @@ -1362,16 +1362,16 @@ windows md5sum - d0befc33613c980a5a3580b8e2b02d11 + 4a6b9cf0f1e2767240de21af3338ffd7 url - https://bitbucket.org/SingularityViewer/libraries/downloads/slvoice-4.6.0009.20030-windows-20140312.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/slvoice-4.6.0009.20030-windows-20140312-rpk.tar.bz2 windows64 md5sum - d0befc33613c980a5a3580b8e2b02d11 + 4a6b9cf0f1e2767240de21af3338ffd7 url - https://bitbucket.org/SingularityViewer/libraries/downloads/slvoice-4.6.0009.20030-windows-20140312.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/slvoice-4.6.0009.20030-windows-20140312-rpk.tar.bz2 diff --git a/scripts/install.py b/scripts/install.py index 288b19b90..5c565ec8d 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -101,8 +101,10 @@ class InstallFile(object): def _is_md5sum_match(self): hasher = md5(file(self.filename, 'rb').read()) - if hasher.hexdigest() == self.md5sum: + md5_sum = hasher.hexdigest() + if md5_sum == self.md5sum: return True + print "Got:", md5_sum, " Expected ", self.md5sum return False def is_match(self, platform): From fbf947fcd701aea98462dbe16538971b2756aafa Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 4 Dec 2014 16:43:52 -0600 Subject: [PATCH 22/55] Pre cmake3.0 support. --- indra/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index d6f0e621c..d01053ba1 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -16,8 +16,10 @@ cmake_minimum_required(VERSION 2.6.2 FATAL_ERROR) # basis to convince CMake to add the proper link directory. This line # can be removed when we use full paths for all libraries. cmake_policy(SET CMP0003 OLD) +if(NOT (CMAKE_MAJOR_VERSION LESS 3)) cmake_policy(SET CMP0048 OLD) cmake_policy(SET CMP0026 OLD) +endif(NOT (CMAKE_MAJOR_VERSION LESS 3)) set(ROOT_PROJECT_NAME "Singularity" CACHE STRING "The root project/makefile/solution name. Defaults to Singularity.") From fd13ccf802eedaed862ecff49fbbcb77ef087c80 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 4 Dec 2014 17:23:27 -0600 Subject: [PATCH 23/55] Support running develop.py without needing to be in indra directory. --- indra/develop.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/indra/develop.py b/indra/develop.py index 28d09674f..48b84c95f 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -111,11 +111,15 @@ class PlatformSetup(object): def build_dirs(self): '''Return the top-level directories in which builds occur. - + if(os.getcwd().rsplit('/', 1)[1] == 'indra'): + prefix = '../' + else: + prefix = '' + This can return more than one directory, e.g. if doing a 32-bit viewer and server build on Linux.''' - return ['../build-' + self.platform()] + return [prefix+'build-' + self.platform()] def cmake_commandline(self, src_dir, build_dir, opts, simple): '''Return the command line to run cmake with.''' @@ -275,9 +279,14 @@ class LinuxSetup(UnixSetup): return 'linux' def build_dirs(self): + if(os.getcwd().rsplit('/', 1)[1] == 'indra'): + prefix = '../' + else: + prefix = '' + platform_build = '%s-%s' % (self.platform(), self.build_type.lower()) - return ['../viewer-' + platform_build] + return [prefix+'viewer-' + platform_build] def cmake_commandline(self, src_dir, build_dir, opts, simple): args = dict( @@ -501,10 +510,15 @@ class WindowsSetup(PlatformSetup): return 'win32' def build_dirs(self): - if self.word_size == 64: - return ['../build-' + self.generator + '-Win64'] + if(os.getcwd().rsplit('\\', 1)[1] == 'indra'): + prefix = '../' else: - return ['../build-' + self.generator] + prefix = '' + + if self.word_size == 64: + return [prefix+'build-' + self.generator + '-Win64'] + else: + return [prefix+'build-' + self.generator] def cmake_commandline(self, src_dir, build_dir, opts, simple): args = dict( From 18e33d226820916e73663175d1742ca0a05cc505 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 4 Dec 2014 17:53:41 -0600 Subject: [PATCH 24/55] Un-break build for older compilers. --- indra/llcommon/llstl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h index fdd2f7f22..bd129e8a3 100644 --- a/indra/llcommon/llstl.h +++ b/indra/llcommon/llstl.h @@ -296,7 +296,7 @@ inline void vector_shrink_to_fit(std::vector& invec) #if defined(LL_WINDOWS) || __cplusplus >= 201103L || (defined(__GXX_EXPERIMENTAL_CXX0X) && __GNUC_MINOR__ >= 5) invec.shrink_to_fit(); #else - invec(invec.begin(), invec.end()).swap(invec); + std::vector(invec).swap(invec); #endif } From ab4561aaccb95742f81653455c2ce0a51feb841c Mon Sep 17 00:00:00 2001 From: Shyotl Date: Mon, 8 Dec 2014 18:46:15 -0600 Subject: [PATCH 25/55] Linux build pass. --- indra/llcommon/llthread.h | 35 +++++++++++++++++++++----- indra/llplugin/llpluginmessagepipe.cpp | 5 ++-- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 8dee66b34..9ad63bbdd 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -27,12 +27,14 @@ #ifndef LL_LLTHREAD_H #define LL_LLTHREAD_H -#define USE_BOOST_MUTEX +#define USE_BOOST_MUTEX 0 + +#define IS_LLCOMMON_INLINE (!LL_COMMON_LINK_SHARED || defined(llcommon_EXPORTS)) #if LL_GNUC // Needed for is_main_thread() when compiling with optimization (relwithdebinfo). // It doesn't hurt to just always specify it though. -#pragma interface +//#pragma interface #endif #include "llapp.h" @@ -188,7 +190,7 @@ protected: //Prefer boost over stl over windows over apr. -#if defined(USE_BOOST_MUTEX) && (BOOST_VERSION >= 103400) //condition_variable_any was added in boost 1.34 +#if USE_BOOST_MUTEX && (BOOST_VERSION >= 103400) //condition_variable_any was added in boost 1.34 //Define BOOST_SYSTEM_NO_DEPRECATED to avoid system_category() and generic_category() dependencies, as those won't be exported. #define BOOST_SYSTEM_NO_DEPRECATED #include @@ -268,7 +270,12 @@ public: { if (inc_lock_if_recursive()) return; + +#if IS_LLCOMMON_INLINE if (AIThreadID::in_main_thread_inline() && LLApp::isRunning()) +#else + if (AIThreadID::in_main_thread() && LLApp::isRunning()) +#endif { if (!LLMutexImpl::try_lock()) { @@ -279,7 +286,11 @@ public: { LLMutexImpl::lock(); } +#if IS_LLCOMMON_INLINE mLockingThread.reset_inline(); +#else + mLockingThread.reset(); +#endif } void unlock() @@ -302,7 +313,11 @@ public: return true; if (!LLMutexImpl::try_lock()) return false; +#if IS_LLCOMMON_INLINE mLockingThread.reset_inline(); +#else + mLockingThread.reset(); +#endif return true; } @@ -321,7 +336,11 @@ public: // Returns true if locked by this thread. bool isSelfLocked() const { +#if IS_LLCOMMON_INLINE return mLockingThread.equals_current_thread_inline(); +#else + return mLockingThread.equals_current_thread(); +#endif } #ifdef NEEDS_MUTEX_IMPL @@ -426,16 +445,20 @@ public: {} ~LLCondition() {} - void LL_COMMON_API wait() + void wait() { +#if IS_LLCOMMON_INLINE if (AIThreadID::in_main_thread_inline()) +#else + if (AIThreadID::in_main_thread()) +#endif wait_main(); else LLConditionVariableImpl::wait(*this); } void signal() { LLConditionVariableImpl::notify_one(); } void broadcast() { LLConditionVariableImpl::notify_all(); } private: - void wait_main(); //Cannot be inline. Uses internal fasttimer. + LL_COMMON_API void wait_main(); //Cannot be inline. Uses internal fasttimer. }; class LLMutexLock @@ -646,7 +669,7 @@ public: void unref() { llassert(mRef > 0); - if (!--mRef) delete this; + if (--mRef == 0) delete this; } S32 getNumRefs() const { diff --git a/indra/llplugin/llpluginmessagepipe.cpp b/indra/llplugin/llpluginmessagepipe.cpp index a00cd63e6..cbe3f1226 100644 --- a/indra/llplugin/llpluginmessagepipe.cpp +++ b/indra/llplugin/llpluginmessagepipe.cpp @@ -120,10 +120,11 @@ LLPluginMessagePipe::~LLPluginMessagePipe() bool LLPluginMessagePipe::addMessage(const std::string &message) { // queue the message for later output - LLMutexLock lock(&mOutputMutex); + //LLMutexLock lock(&mOutputMutex); + mOutputMutex.lock(); mOutput += message; mOutput += MESSAGE_DELIMITER; // message separator - + mOutputMutex.unlock(); return true; } From e2cd11ccd78675aeb2f8c142961f7b5a42668aed Mon Sep 17 00:00:00 2001 From: Shyotl Date: Mon, 8 Dec 2014 21:40:11 -0600 Subject: [PATCH 26/55] Fix up some small typos and avoid c++11's string::back()/pop_back() calls for now. --- indra/llaudio/llaudioengine_fmodex.cpp | 2 +- indra/llaudio/llaudioengine_fmodstudio.cpp | 2 +- indra/llaudio/llstreamingaudio_fmodex.cpp | 22 +++++++++---------- indra/llaudio/llstreamingaudio_fmodstudio.cpp | 18 +++++++-------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index 25d5754fb..8b71012ba 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -373,7 +373,7 @@ bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) if (NULL == getenv("LL_BAD_FMOD_OSS")) /*Flawfinder: ignore*/ { LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL; - if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_OSS) == FMOD_OK) && + if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_OSS)) == FMOD_OK && (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) { LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL; diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index 4da18d25e..47d54fd3b 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -372,7 +372,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) if (NULL == getenv("LL_BAD_FMOD_OSS")) /*Flawfinder: ignore*/ { LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL; - if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_OSS) == FMOD_OK) && + if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_OSS)) == FMOD_OK && (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) { LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL; diff --git a/indra/llaudio/llstreamingaudio_fmodex.cpp b/indra/llaudio/llstreamingaudio_fmodex.cpp index 9c4aad93b..0716e09d5 100644 --- a/indra/llaudio/llstreamingaudio_fmodex.cpp +++ b/indra/llaudio/llstreamingaudio_fmodex.cpp @@ -289,8 +289,8 @@ void LLStreamingAudio_FMODEX::update() case(FMOD_TAGDATATYPE_STRING): { std::string out = rawstr_to_utf8(std::string((char*)tag.data,tag.datalen)); - if (out.length() && out.back() == 0) - out.pop_back(); + if (out.length() && out[out.size() - 1] == 0) + out.erase(out.size() - 1); (*mMetaData)[name]=out; llinfos << tag.name << "(RAW): " << out << llendl; } @@ -301,8 +301,8 @@ void LLStreamingAudio_FMODEX::update() if (tag.datalen > 3 && ((char*)tag.data)[0] == 0xEF && ((char*)tag.data)[1] == 0xBB && ((char*)tag.data)[2] == 0xBF) offs = 3; std::string out((char*)tag.data + offs, tag.datalen - offs); - if (out.length() && out.back() == 0) - out.pop_back(); + if (out.length() && out[out.size() - 1] == 0) + out.erase(out.size() - 1); (*mMetaData)[name] = out; llinfos << tag.name << "(UTF8): " << out << llendl; } @@ -310,8 +310,8 @@ void LLStreamingAudio_FMODEX::update() case(FMOD_TAGDATATYPE_STRING_UTF16): { std::string out = utf16input_to_utf8((char*)tag.data, tag.datalen, UTF16); - if (out.length() && out.back() == 0) - out.pop_back(); + if (out.length() && out[out.size() - 1] == 0) + out.erase(out.size() - 1); (*mMetaData)[name] = out; llinfos << tag.name << "(UTF16): " << out << llendl; } @@ -319,8 +319,8 @@ void LLStreamingAudio_FMODEX::update() case(FMOD_TAGDATATYPE_STRING_UTF16BE): { std::string out = utf16input_to_utf8((char*)tag.data, tag.datalen, UTF16BE); - if (out.length() && out.back() == 0) - out.pop_back(); + if (out.length() && out[out.size() - 1] == 0) + out.erase(out.size() - 1); (*mMetaData)[name] = out; llinfos << tag.name << "(UTF16BE): " << out << llendl; } @@ -568,7 +568,7 @@ void LLStreamingAudio_FMODEX::setBufferSizes(U32 streambuffertime, U32 decodebuf Check_FMOD_Error(mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES), "FMOD::System::setStreamBufferSize"); FMOD_ADVANCEDSETTINGS settings; memset(&settings,0,sizeof(settings)); - settings.cbSize=sizeof(settings); + settings.cbsize=sizeof(settings); settings.defaultDecodeBufferSize = decodebuffertime;//ms Check_FMOD_Error(mSystem->setAdvancedSettings(&settings), "FMOD::System::setAdvancedSettings"); } @@ -576,10 +576,10 @@ void LLStreamingAudio_FMODEX::setBufferSizes(U32 streambuffertime, U32 decodebuf bool LLStreamingAudio_FMODEX::releaseDeadStreams() { // Kill dead internet streams, if possible - std::list::iterator iter; + std::list::iterator iter; for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();) { - LLAudioStreamManagerFMODSTUDIO *streamp = *iter; + LLAudioStreamManagerFMODEX *streamp = *iter; if (streamp->stopStream()) { llinfos << "Closed dead stream" << llendl; diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.cpp b/indra/llaudio/llstreamingaudio_fmodstudio.cpp index dbfaaa48d..5a530c557 100644 --- a/indra/llaudio/llstreamingaudio_fmodstudio.cpp +++ b/indra/llaudio/llstreamingaudio_fmodstudio.cpp @@ -142,7 +142,7 @@ LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) : // Leave the net buffer properties at the default. //FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80); - Check_FMOD_Error(system->createChannelGroup("stream", &mStreamGroup), "FMOD::System::createChannelGroup"); + Check_FMOD_Error(mSystem->createChannelGroup("stream", &mStreamGroup), "FMOD::System::createChannelGroup"); FMOD_DSP_DESCRIPTION dspdesc; memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION)); //Zero out everything @@ -353,8 +353,8 @@ void LLStreamingAudio_FMODSTUDIO::update() case(FMOD_TAGDATATYPE_STRING): { std::string out = rawstr_to_utf8(std::string((char*)tag.data,tag.datalen)); - if (out.length() && out.back() == 0) - out.pop_back(); + if (out.length() && out[out.size() - 1] == 0) + out.erase(out.size() - 1); (*mMetaData)[name]=out; llinfos << tag.name << "(RAW): " << out << llendl; } @@ -365,8 +365,8 @@ void LLStreamingAudio_FMODSTUDIO::update() if (tag.datalen > 3 && ((char*)tag.data)[0] == 0xEF && ((char*)tag.data)[1] == 0xBB && ((char*)tag.data)[2] == 0xBF) offs = 3; std::string out((char*)tag.data + offs, tag.datalen - offs); - if (out.length() && out.back() == 0) - out.pop_back(); + if (out.length() && out[out.size() - 1] == 0) + out.erase(out.size() - 1); (*mMetaData)[name] = out; llinfos << tag.name << "(UTF8): " << out << llendl; } @@ -374,8 +374,8 @@ void LLStreamingAudio_FMODSTUDIO::update() case(FMOD_TAGDATATYPE_STRING_UTF16): { std::string out = utf16input_to_utf8((char*)tag.data, tag.datalen, UTF16); - if (out.length() && out.back() == 0) - out.pop_back(); + if (out.length() && out[out.size() - 1] == 0) + out.erase(out.size() - 1); (*mMetaData)[name] = out; llinfos << tag.name << "(UTF16): " << out << llendl; } @@ -383,8 +383,8 @@ void LLStreamingAudio_FMODSTUDIO::update() case(FMOD_TAGDATATYPE_STRING_UTF16BE): { std::string out = utf16input_to_utf8((char*)tag.data, tag.datalen, UTF16BE); - if (out.length() && out.back() == 0) - out.pop_back(); + if (out.length() && out[out.size() - 1] == 0) + out.erase(out.size() - 1); (*mMetaData)[name] = out; llinfos << tag.name << "(UTF16BE): " << out << llendl; } From b680b531287cae759174b43fab2f8cfbd9e702ee Mon Sep 17 00:00:00 2001 From: Shyotl Date: Mon, 8 Dec 2014 23:34:42 -0600 Subject: [PATCH 27/55] Update vstool executable path. --- indra/develop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/develop.py b/indra/develop.py index 48b84c95f..141c0652d 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -671,7 +671,7 @@ class WindowsSetup(PlatformSetup): if prev_build == self.build_type: # Only run vstool if the build type has changed. continue - vstool_cmd = (os.path.join('tools','vstool','VSTool.exe') + + vstool_cmd = (os.path.join('indra','tools','vstool','VSTool.exe') + ' --solution ' + os.path.join(build_dir,'Singularity.sln') + ' --config ' + self.build_type + From f014c8207c891faa0b03e64a5dbcffbdbd69a85e Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 9 Dec 2014 01:28:23 -0600 Subject: [PATCH 28/55] Try path.os.basename/split instead of string.rsplit --- indra/develop.py | 13 +++++++++---- scripts/install.py | 12 ++++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/indra/develop.py b/indra/develop.py index 141c0652d..94c8db5ea 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -111,7 +111,7 @@ class PlatformSetup(object): def build_dirs(self): '''Return the top-level directories in which builds occur. - if(os.getcwd().rsplit('/', 1)[1] == 'indra'): + if(os.path.basename(os.path.normpath(os.getcwd())) == 'indra'): prefix = '../' else: prefix = '' @@ -279,7 +279,7 @@ class LinuxSetup(UnixSetup): return 'linux' def build_dirs(self): - if(os.getcwd().rsplit('/', 1)[1] == 'indra'): + if(os.path.basename(os.path.normpath(os.getcwd())) == 'indra'): prefix = '../' else: prefix = '' @@ -510,7 +510,7 @@ class WindowsSetup(PlatformSetup): return 'win32' def build_dirs(self): - if(os.getcwd().rsplit('\\', 1)[1] == 'indra'): + if(os.path.basename(os.path.normpath(os.getcwd())) == 'indra'): prefix = '../' else: prefix = '' @@ -671,7 +671,12 @@ class WindowsSetup(PlatformSetup): if prev_build == self.build_type: # Only run vstool if the build type has changed. continue - vstool_cmd = (os.path.join('indra','tools','vstool','VSTool.exe') + + + if(os.path.basename(os.path.normpath(os.getcwd())) == 'indra'): + tool_path = os.path.join('tools','vstool','VSTool.exe') + else: + tool_path = os.path.join('indra','tools','vstool','VSTool.exe') + vstool_cmd = (tool_path + ' --solution ' + os.path.join(build_dir,'Singularity.sln') + ' --config ' + self.build_type + diff --git a/scripts/install.py b/scripts/install.py index 5c565ec8d..f43bc8dae 100755 --- a/scripts/install.py +++ b/scripts/install.py @@ -126,16 +126,20 @@ class InstallFile(object): return True def fetch_local(self): - #print "Looking for:",self.filename + cache_path, cache_file = os.path.split(self.filename) + cache_path, cache_folder = os.path.split(os.path.normpath(cache_path)) + cache_file = os.path.join(cache_path, cache_folder.rsplit('.',1)[0] + ".", cache_file) + #print "Looking for:",cache_file + if not os.path.exists(self.filename): pass elif self.md5sum and not self._is_md5sum_match(): - print "md5 mismatch:", self.filename + print "md5 mismatch:", cache_file os.remove(self.filename) else: - print "Found matching package:", self.filename + print "Found matching package:", cache_file return - print "Downloading",self.url,"to local file",self.filename + print "Downloading",self.url,"to local file",cache_file request = urllib2.Request(self.url) From d571b8be81c8bf3fc2512e86001978a1476bf759 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 9 Dec 2014 16:13:59 -0600 Subject: [PATCH 29/55] Viewer manifest cleanup/updating --- indra/lib/python/indra/util/llmanifest.py | 122 +++++-- indra/newview/viewer_manifest.py | 417 ++++++++++++++-------- 2 files changed, 349 insertions(+), 190 deletions(-) diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py index 625d1456f..d4e325062 100644 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -41,6 +41,14 @@ import tarfile import errno import subprocess +class ManifestError(RuntimeError): + """Use an exception more specific than generic Python RuntimeError""" + pass + +class MissingError(ManifestError): + """You specified a file that doesn't exist""" + pass + def path_ancestors(path): drive, path = os.path.splitdrive(os.path.normpath(path)) result = [] @@ -245,15 +253,25 @@ def main(): for opt in args: print "Option:", opt, "=", args[opt] + # Build base package. + touch = args.get('touch') + if touch: + print 'Creating base package' + args['package_id'] = "" # base package has no package ID wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args) wm.do(*args['actions']) + # Store package file for later if making touched file. + base_package_file = "" + if touch: + print 'Created base package ', wm.package_file + base_package_file = "" + wm.package_file # Write out the package file in this format, so that it can easily be called # and used in a .bat file - yeah, it sucks, but this is the simplest... touch = args.get('touch') if touch: fp = open(touch, 'w') - fp.write('set package_file=%s\n' % wm.package_file) + fp.write('set package_file=%s\n' % base_package_file) fp.close() print 'touched', touch return 0 @@ -390,8 +408,8 @@ class LLManifest(object): def run_command(self, command): """ Runs an external command, and returns the output. Raises - an exception if the command reurns a nonzero status code. For - debugging/informational purpoases, prints out the command's + an exception if the command returns a nonzero status code. For + debugging/informational purposes, prints out the command's output as it is received.""" print "Running command:", command fd = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) @@ -404,7 +422,7 @@ class LLManifest(object): print lines[-1].rstrip('\n'), output = ''.join(lines) if fd.returncode: - raise RuntimeError( + raise ManifestError( "Command %s returned non-zero status (%s) \noutput:\n%s" % (command, fd.returncode, output) ) return output @@ -414,14 +432,24 @@ class LLManifest(object): a) verify that you really have created it b) schedule it for cleanup""" if not os.path.exists(path): - raise RuntimeError, "Should be something at path " + path + raise ManifestError, "Should be something at path " + path self.created_paths.append(path) - def put_in_file(self, contents, dst): + def put_in_file(self, contents, dst, src=None): # write contents as dst - f = open(self.dst_path_of(dst), "wb") - f.write(contents) - f.close() + dst_path = self.dst_path_of(dst) + f = open(dst_path, "wb") + try: + f.write(contents) + finally: + f.close() + + # Why would we create a file in the destination tree if not to include + # it in the installer? The default src=None (plus the fact that the + # src param is last) is to preserve backwards compatibility. + if src: + self.file_list.append([src, dst_path]) + return dst_path def replace_in(self, src, dst=None, searchdict={}): if dst == None: @@ -569,7 +597,7 @@ class LLManifest(object): except (IOError, os.error), why: errors.append((srcname, dstname, why)) if errors: - raise RuntimeError, errors + raise ManifestError, errors def cmakedirs(self, path): @@ -617,11 +645,10 @@ class LLManifest(object): def check_file_exists(self, path): if not os.path.exists(path) and not os.path.islink(path): - raise RuntimeError("Path %s doesn't exist" % ( - os.path.normpath(os.path.join(os.getcwd(), path)),)) + raise MissingError("Path %s doesn't exist" % (os.path.abspath(path),)) - wildcard_pattern = re.compile('\*') + wildcard_pattern = re.compile(r'\*') def expand_globs(self, src, dst): src_list = glob.glob(src) src_re, d_template = self.wildcard_regex(src.replace('\\', '/'), @@ -630,41 +657,72 @@ class LLManifest(object): d = src_re.sub(d_template, s.replace('\\', '/')) yield os.path.normpath(s), os.path.normpath(d) + def path2basename(self, path, file): + """ + It is a common idiom to write: + self.path(os.path.join(somedir, somefile), somefile) + + So instead you can write: + self.path2basename(somedir, somefile) + + Note that this is NOT the same as: + self.path(os.path.join(somedir, somefile)) + + which is the same as: + temppath = os.path.join(somedir, somefile) + self.path(temppath, temppath) + """ + return self.path(os.path.join(path, file), file) + def path(self, src, dst=None): sys.stdout.write("Processing %s => %s ... " % (src, dst)) sys.stdout.flush() if src == None: - raise RuntimeError("No source file, dst is " + dst) + raise ManifestError("No source file, dst is " + dst) if dst == None: dst = src dst = os.path.join(self.get_dst_prefix(), dst) - count = 0 - is_glob = False - # look under each prefix for matching paths - paths = set([os.path.join(self.get_src_prefix(), src), - os.path.join(self.get_artwork_prefix(), src), - os.path.join(self.get_build_prefix(), src)]) - for path in paths: - if self.wildcard_pattern.search(path): - is_glob = True - for s,d in self.expand_globs(path, dst): + def try_path(src): + # expand globs + count = 0 + if self.wildcard_pattern.search(src): + for s,d in self.expand_globs(src, dst): assert(s != d) count += self.process_file(s, d) else: + # if we're specifying a single path (not a glob), + # we should error out if it doesn't exist + self.check_file_exists(src) # if it's a directory, recurse through it - if os.path.isdir(path): - count += self.process_directory(path, dst) - elif os.path.exists(path): - count += self.process_file(path, dst) + if os.path.isdir(src): + count += self.process_directory(src, dst) + else: + count += self.process_file(src, dst) + return count - # if we're specifying a single path (not a glob), - # we should error out if it doesn't exist - if count == 0 and not is_glob: - raise RuntimeError("No files match %s\n" % str(paths)) + for pfx in self.get_src_prefix(), self.get_artwork_prefix(), self.get_build_prefix(): + try: + count = try_path(os.path.join(pfx, src)) + except MissingError: + # If src isn't a wildcard, and if that file doesn't exist in + # this pfx, try next pfx. + count = 0 + continue + + # Here try_path() didn't raise MissingError. Did it process any files? + if count: + break + # Even though try_path() didn't raise MissingError, it returned 0 + # files. src is probably a wildcard meant for some other pfx. Loop + # back to try the next. print "%d files" % count + # Let caller check whether we processed as many files as expected. In + # particular, let caller notice 0. + return count + def do(self, *actions): self.actions = actions self.construct() diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 342318b26..5828e8c90 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -174,6 +174,8 @@ class ViewerManifest(LLManifest): return " ".join((channel_flags, grid_flags, setting_flags)).strip() + def icon_path(self): + return "../../indra/newview/res/" class WindowsManifest(ViewerManifest): def is_win64(self): return self.args.get('arch') == "x86_64" @@ -191,9 +193,9 @@ class WindowsManifest(ViewerManifest): release_lib_dir = "Release" # Plugin host application - self.path(os.path.join(os.pardir, - 'llplugin', 'slplugin', self.args['configuration'], "SLPlugin.exe"), - "SLPlugin.exe") + self.path2basename(os.path.join(os.pardir, + 'llplugin', 'slplugin', self.args['configuration']), + "SLplugin.exe") # Plugin volume control if not self.is_win64() and self.prefix(src=self.args['configuration'], dst=""): @@ -505,9 +507,10 @@ class DarwinManifest(ViewerManifest): self.path(self.args['configuration'] + "/" + self.app_name() + ".app", dst="") if self.prefix(src="", dst="Contents"): # everything goes in Contents + # copy additional libs in /Contents/MacOS/ - self.path("../../libraries/universal-darwin/lib/release/libndofdev.dylib", dst="Resources/libndofdev.dylib") - self.path("../../libraries/universal-darwin/lib/release/libhunspell-1.3.0.dylib", dst="Resources/libhunspell-1.3.0.dylib") + self.path("../packages/libraries/universal-darwin/lib/release/libndofdev.dylib", dst="Resources/libndofdev.dylib") + self.path("../packages/libraries/universal-darwin/lib/release/libhunspell-1.3.0.dylib", dst="Resources/libhunspell-1.3.0.dylib") # most everything goes in the Resources directory if self.prefix(src="", dst="Resources"): @@ -521,7 +524,10 @@ class DarwinManifest(ViewerManifest): self.path("featuretable_mac.txt") self.path("SecondLife.nib") - self.path(("../newview/res/%s_icon.icns" % self.viewer_branding_id()), dst=("%s_icon.icns" % self.viewer_branding_id())) + icon_path = self.icon_path() + if self.prefix(src=icon_path, dst="") : + self.path("%s_icon.icns" % self.viewer_branding_id()) + self.end_prefix(icon_path) # Translations self.path("English.lproj") @@ -541,27 +547,55 @@ class DarwinManifest(ViewerManifest): self.path("uk.lproj") self.path("zh-Hans.lproj") - # SLVoice and vivox lols - self.path("vivox-runtime/universal-darwin/SLVoice", "SLVoice") - self.path("vivox-runtime/universal-darwin/ca-bundle.crt", "ca-bundle.crt") - self.path("vivox-runtime/universal-darwin/libortp.dylib", "libortp.dylib") - self.path("vivox-runtime/universal-darwin/libsndfile.dylib", "libsndfile.dylib") - self.path("vivox-runtime/universal-darwin/libvivoxoal.dylib", "libvivoxoal.dylib") - self.path("vivox-runtime/universal-darwin/libvivoxplatform.dylib", "libvivoxplatform.dylib") - self.path("vivox-runtime/universal-darwin/libvivoxsdk.dylib", "libvivoxsdk.dylib") + def path_optional(src, dst): + """ + For a number of our self.path() calls, not only do we want + to deal with the absence of src, we also want to remember + which were present. Return either an empty list (absent) + or a list containing dst (present). Concatenate these + return values to get a list of all libs that are present. + """ + if self.path(src, dst): + return [dst] + print "Skipping %s" % dst + return [] - self.path("../llcommon/" + self.args['configuration'] + "/libllcommon.dylib", "libllcommon.dylib") + libdir = "../packages/libraries/universal-darwin/lib/release" + # dylibs is a list of all the .dylib files we expect to need + # in our bundled sub-apps. For each of these we'll create a + # symlink from sub-app/Contents/Resources to the real .dylib. + # Need to get the llcommon dll from any of the build directories as well. + + libfile = "libllcommon.dylib" - libfile = "lib%s.dylib" - libdir = "../../libraries/universal-darwin/lib/release" + dylibs = path_optional(self.find_existing_file(os.path.join(os.pardir, + "llcommon", + self.args['configuration'], + libfile), + os.path.join(libdir, libfile)), + dst=libfile) - for libfile in ("libapr-1.0.dylib", + for libfile in ( + "libapr-1.0.dylib", "libaprutil-1.0.dylib", "libcollada14dom.dylib", "libexpat.1.5.2.dylib", + "libexception_handler.dylib", "libGLOD.dylib", - "libexception_handler.dylib"): - self.path(os.path.join(libdir, libfile), libfile) + ): + dylibs += path_optional(os.path.join(libdir, libfile), libfile) + + # SLVoice and vivox lols, no symlinks needed + for libfile in ( + 'libortp.dylib', + 'libsndfile.dylib', + 'libvivoxoal.dylib', + 'libvivoxsdk.dylib', + 'libvivoxplatform.dylib', + 'ca-bundle.crt', + 'SLVoice', + ): + self.path2basename(libdir, libfile) # For using FMOD for sound...but, fmod is proprietary so some might not use it... try: @@ -571,40 +605,48 @@ class DarwinManifest(ViewerManifest): print "Skipping libfmodwrapper.dylib - not found" pass - # And now FMOD Ex! - try: - self.path("libfmodex.dylib", "libfmodex.dylib") - pass - except: - print "Skipping libfmodex.dylib - not found" - pass - # plugin launcher - self.path("../llplugin/slplugin/" + self.args['configuration'] + "/SLPlugin.app", "SLPlugin.app") + # dylibs that vary based on configuration + if self.args['configuration'].lower() == 'debug': + for libfile in ( + "libfmodexL.dylib", + ): + dylibs += path_optional(os.path.join("../packages/lib/debug", + libfile), libfile) + else: + for libfile in ( + "libfmodex.dylib", + ): + dylibs += path_optional(os.path.join("../packages/lib/release", + libfile), libfile) - # dependencies on shared libs - slplugin_res_path = self.dst_path_of("SLPlugin.app/Contents/Resources") - for libfile in ("libllcommon.dylib", - "libapr-1.0.dylib", - "libaprutil-1.0.dylib", - "libexpat.1.5.2.dylib", - "libexception_handler.dylib"): - target_lib = os.path.join('../../..', libfile) - self.run_command("ln -sf %(target)r %(link)r" % - {'target': target_lib, - 'link' : os.path.join(slplugin_res_path, libfile)} - ) - #self.run_command("ln -sf %(target)r %(link)r" % - # {'target': target_lib, - # 'link' : os.path.join(mac_crash_logger_res_path, libfile)} - # ) + # our apps + for app_bld_dir, app in (#("mac_crash_logger", "mac-crash-logger.app"), + # plugin launcher + (os.path.join("llplugin", "slplugin"), "SLPlugin.app"), + ): + self.path2basename(os.path.join(os.pardir, + app_bld_dir, self.args['configuration']), + app) + + # our apps dependencies on shared libs + # for each app, for each dylib we collected in dylibs, + # create a symlink to the real copy of the dylib. + resource_path = self.dst_path_of(os.path.join(app, "Contents", "Resources")) + for libfile in dylibs: + symlinkf(os.path.join(os.pardir, os.pardir, os.pardir, libfile), + os.path.join(resource_path, libfile)) # plugins if self.prefix(src="", dst="llplugin"): - self.path("../plugins/filepicker/" + self.args['configuration'] + "/basic_plugin_filepicker.dylib", "basic_plugin_filepicker.dylib") - self.path("../plugins/quicktime/" + self.args['configuration'] + "/media_plugin_quicktime.dylib", "media_plugin_quicktime.dylib") - self.path("../plugins/webkit/" + self.args['configuration'] + "/media_plugin_webkit.dylib", "media_plugin_webkit.dylib") - self.path("../../libraries/universal-darwin/lib/release/libllqtwebkit.dylib", "libllqtwebkit.dylib") + self.path2basename(os.path.join(os.pardir,"plugins", "filepicker", self.args['configuration']), + "basic_plugin_filepicker.dylib") + self.path2basename(os.path.join(os.pardir,"plugins", "quicktime", self.args['configuration']), + "media_plugin_quicktime.dylib") + self.path2basename(os.path.join(os.pardir,"plugins", "webkit", self.args['configuration']), + "media_plugin_webkit.dylib") + self.path2basename(os.path.join(os.pardir,"packages", "libraries", "universal-darwin", "lib", "release"), + "libllqtwebkit.dylib") self.end_prefix("llplugin") @@ -687,64 +729,88 @@ class DarwinManifest(ViewerManifest): 'vol':volname}) # mount the image and get the name of the mount point and device node - hdi_output = self.run_command('hdiutil attach -private "' + sparsename + '"') - devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip() - volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip() + hdi_output = self.run_command('hdiutil attach -private %r' % sparsename) + try: + devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip() + volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip() - # Copy everything in to the mounted .dmg + if devfile != '/dev/disk1': + # adding more debugging info based upon nat's hunches to the + # logs to help track down 'SetFile -a V' failures -brad + print "WARNING: 'SetFile -a V' command below is probably gonna fail" - if self.default_channel_for_brand() and not self.default_grid(): - app_name = self.app_name() + " " + self.args['grid'] - else: - app_name = channel_standin.strip() + # Copy everything in to the mounted .dmg - # Hack: - # Because there is no easy way to coerce the Finder into positioning - # the app bundle in the same place with different app names, we are - # adding multiple .DS_Store files to svn. There is one for release, - # one for release candidate and one for first look. Any other channels - # will use the release .DS_Store, and will look broken. - # - Ambroff 2008-08-20 - # Added a .DS_Store for snowglobe - Merov 2009-06-17 + if self.default_channel_for_brand() and not self.default_grid(): + app_name = self.app_name() + " " + self.args['grid'] + else: + app_name = channel_standin.strip() - # We have a single branded installer for all snowglobe channels so snowglobe logic is a bit different - if (self.app_name()=="Snowglobe"): - dmg_template = os.path.join ('installers', 'darwin', 'snowglobe-dmg') - else: - dmg_template = os.path.join( - 'installers', - 'darwin', - '%s-dmg' % "".join(self.channel_unique().split()).lower()) + # Hack: + # Because there is no easy way to coerce the Finder into positioning + # the app bundle in the same place with different app names, we are + # adding multiple .DS_Store files to svn. There is one for release, + # one for release candidate and one for first look. Any other channels + # will use the release .DS_Store, and will look broken. + # - Ambroff 2008-08-20 + # Added a .DS_Store for snowglobe - Merov 2009-06-17 - if not os.path.exists (self.src_path_of(dmg_template)): - dmg_template = os.path.join ('installers', 'darwin', 'release-dmg') + # We have a single branded installer for all snowglobe channels so snowglobe logic is a bit different + if (self.app_name()=="Snowglobe"): + dmg_template = os.path.join ('installers', 'darwin', 'snowglobe-dmg') + else: + dmg_template = os.path.join( + 'installers', + 'darwin', + '%s-dmg' % "".join(self.channel_unique().split()).lower()) - for s,d in {self.get_dst_prefix():app_name + ".app", - os.path.join(dmg_template, "_VolumeIcon.icns"): ".VolumeIcon.icns", - os.path.join(dmg_template, "background.jpg"): "background.jpg", - os.path.join(dmg_template, "_DS_Store"): ".DS_Store"}.items(): - print "Copying to dmg", s, d - self.copy_action(self.src_path_of(s), os.path.join(volpath, d)) + if not os.path.exists (self.src_path_of(dmg_template)): + dmg_template = os.path.join ('installers', 'darwin', 'release-dmg') - # Hide the background image, DS_Store file, and volume icon file (set their "visible" bit) - self.run_command('SetFile -a V "' + os.path.join(volpath, ".VolumeIcon.icns") + '"') - self.run_command('SetFile -a V "' + os.path.join(volpath, "background.jpg") + '"') - self.run_command('SetFile -a V "' + os.path.join(volpath, ".DS_Store") + '"') + for s,d in {self.get_dst_prefix():app_name + ".app", + os.path.join(dmg_template, "_VolumeIcon.icns"): ".VolumeIcon.icns", + os.path.join(dmg_template, "background.jpg"): "background.jpg", + os.path.join(dmg_template, "_DS_Store"): ".DS_Store"}.items(): + print "Copying to dmg", s, d + self.copy_action(self.src_path_of(s), os.path.join(volpath, d)) - # Create the alias file (which is a resource file) from the .r - self.run_command('rez "' + self.src_path_of("installers/darwin/release-dmg/Applications-alias.r") + '" -o "' + os.path.join(volpath, "Applications") + '"') + # Hide the background image, DS_Store file, and volume icon file (set their "visible" bit) + for f in ".VolumeIcon.icns", "background.jpg", ".DS_Store": + pathname = os.path.join(volpath, f) + # We've observed mysterious "no such file" failures of the SetFile + # command, especially on the first file listed above -- yet + # subsequent inspection of the target directory confirms it's + # there. Timing problem with copy command? Try to handle. + for x in xrange(3): + if os.path.exists(pathname): + print "Confirmed existence: %r" % pathname + break + print "Waiting for %s copy command to complete (%s)..." % (f, x+1) + sys.stdout.flush() + time.sleep(1) + # If we fall out of the loop above without a successful break, oh + # well, possibly we've mistaken the nature of the problem. In any + # case, don't hang up the whole build looping indefinitely, let + # the original problem manifest by executing the desired command. + self.run_command('SetFile -a V %r' % pathname) - # Set the alias file's alias and custom icon bits - self.run_command('SetFile -a AC "' + os.path.join(volpath, "Applications") + '"') + # Create the alias file (which is a resource file) from the .r + self.run_command('Rez %r -o %r' % + (self.src_path_of("installers/darwin/release-dmg/Applications-alias.r"), + os.path.join(volpath, "Applications"))) - # Set the disk image root's custom icon bit - self.run_command('SetFile -a C "' + volpath + '"') + # Set the alias file's alias and custom icon bits + self.run_command('SetFile -a AC "' + os.path.join(volpath, "Applications") + '"') - # Unmount the image - self.run_command('hdiutil detach -force "' + devfile + '"') + # Set the disk image root's custom icon bit + self.run_command('SetFile -a C "' + volpath + '"') + finally: + # Unmount the image + self.run_command('hdiutil detach -force "' + devfile + '"') print "Converting temp disk image to final disk image" self.run_command('hdiutil convert "%(sparse)s" -format UDZO -imagekey zlib-level=9 -o "%(final)s"' % {'sparse':sparsename, 'final':finalname}) + self.run_command('hdiutil internet-enable -yes "%(final)s"' % {'final':finalname}) # get rid of the temp file self.package_file = finalname self.remove(sparsename) @@ -764,10 +830,12 @@ class LinuxManifest(ViewerManifest): self.path("client-readme-voice.txt","README-linux-voice.txt") self.path("client-readme-joystick.txt","README-linux-joystick.txt") self.path("wrapper.sh",self.wrapper_name()) - self.path("handle_secondlifeprotocol.sh") - self.path("register_secondlifeprotocol.sh") - self.path("refresh_desktop_app_entry.sh") - self.path("launch_url.sh") + if self.prefix(src="", dst="etc"): + self.path("handle_secondlifeprotocol.sh") + self.path("register_secondlifeprotocol.sh") + self.path("refresh_desktop_app_entry.sh") + self.path("launch_url.sh") + self.end_prefix("etc") self.path("install.sh") self.end_prefix("linux_tools") @@ -779,9 +847,11 @@ class LinuxManifest(ViewerManifest): # self.path("secondlife-stripped","bin/"+self.binary_name()) #else: # self.path("secondlife-bin","bin/"+self.binary_name()) - self.path("secondlife-bin","bin/"+self.binary_name()) + if self.prefix(src="", dst="bin"): + self.path("secondlife-bin",self.binary_name()) + self.path2basename("../llplugin/slplugin", "SLPlugin") + self.end_prefix("bin") - self.path("../llplugin/slplugin/SLPlugin", "bin/SLPlugin") if self.prefix("res-sdl"): self.path("*") # recurse @@ -789,9 +859,9 @@ class LinuxManifest(ViewerManifest): # plugins if self.prefix(src="", dst="bin/llplugin"): - self.path("../plugins/filepicker/libbasic_plugin_filepicker.so", "libbasic_plugin_filepicker.so") - self.path("../plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so") - self.path("../plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so") + self.path2basename("../plugins/filepicker", "libbasic_plugin_filepicker.so") + self.path2basename("../plugins/webkit", "libmedia_plugin_webkit.so") + self.path("../plugins/gstreamer010", "libmedia_plugin_gstreamer.so") self.end_prefix("bin/llplugin") self.path("featuretable_linux.txt") @@ -860,10 +930,30 @@ class Linux_i686Manifest(LinuxManifest): def construct(self): super(Linux_i686Manifest, self).construct() - self.path("../llcommon/libllcommon.so", "lib/libllcommon.so") + if not self.path("../llcommon/libllcommon.so", "lib/libllcommon.so"): + print "Skipping llcommon.so (assuming llcommon was linked statically)" - if (not self.standalone()) and self.prefix("../../libraries/i686-linux/lib/release", dst="lib"): + if (not self.standalone()) and self.prefix("../packages/lib/release", dst="lib"): + self.prefix("../packages/libraries/i686-linux/lib/release", dst="lib") + self.path("libapr-1.so*") + self.path("libaprutil-1.so*") + self.path("libdb*.so") + self.path("libexpat.so*") + self.path("libglod.so") + self.path("libuuid.so*") + self.path("libSDL-1.2.so*") + self.path("libdirectfb-1.*.so*") + self.path("libfusion-1.*.so*") + self.path("libdirect-1.*.so*") + self.path("libhunspell-*.so.*") + + self.path("libalut.so") + self.path("libopenal.so.1") + + self.path("libtcmalloc_minimal.so.0") #formerly called google perf tools + self.path("libtcmalloc_minimal.so.0.2.2") + try: self.path("libfmod-3.75.so") pass @@ -871,27 +961,6 @@ class Linux_i686Manifest(LinuxManifest): print "Skipping libfmod-3.75.so - not found" pass - self.path("libELFIO.so") - self.path("libSDL-1.2.so*") - self.path("libapr-1.so*") - self.path("libaprutil-1.so*") - self.path("libcollada14dom.so.2.2", "libcollada14dom.so") - self.path("libcrypto.so*") - self.path("libdb*.so") - self.path("libdirect-1.*.so*") - self.path("libdirectfb-1.*.so*") - self.path("libfusion-1.*.so*") - self.path("libglod.so") - self.path("libminizip.so.1.2.3", "libminizip.so"); - self.path("libexpat.so*") - self.path("libhunspell-*.so.*") - self.path("libssl.so*") - self.path("libuuid.so*") - self.path("libalut.so") - self.path("libopenal.so.1") - self.path("libtcmalloc_minimal.so.0") - self.path("libtcmalloc_minimal.so.0.2.2") - # Boost self.path("libboost_context-mt.so.*") self.path("libboost_filesystem-mt.so.*") @@ -900,6 +969,12 @@ class Linux_i686Manifest(LinuxManifest): self.path("libboost_signals-mt.so.*") self.path("libboost_system-mt.so.*") self.path("libboost_thread-mt.so.*") + + self.path("libcollada14dom.so.2.2", "libcollada14dom.so") + self.path("libcrypto.so*") + self.path("libELFIO.so") + self.path("libminizip.so.1.2.3", "libminizip.so"); + self.path("libssl.so*") if 'extra_libraries' in self.args: path_list = self.args['extra_libraries'].split('|') @@ -909,12 +984,13 @@ class Linux_i686Manifest(LinuxManifest): self.path(src_path, dst_path) self.end_prefix("lib") + self.end_prefix("lib") # Vivox runtimes - if self.prefix(src="vivox-runtime/i686-linux", dst="bin"): + if self.prefix(src="../packages/lib/release", dst="bin"): self.path("SLVoice") - self.end_prefix() - if self.prefix(src="vivox-runtime/i686-linux", dst="lib"): + self.end_prefix("bin") + if self.prefix(src="../packages/lib/release", dst="lib"): self.path("libortp.so") self.path("libvivoxsdk.so") self.end_prefix("lib") @@ -924,25 +1000,23 @@ class Linux_x86_64Manifest(LinuxManifest): def construct(self): super(Linux_x86_64Manifest, self).construct() - self.path("../llcommon/libllcommon.so", "lib64/libllcommon.so") + if not self.path("../llcommon/libllcommon.so", "lib64/libllcommon.so"): + print "Skipping llcommon.so (assuming llcommon was linked statically)" - if (not self.standalone()) and self.prefix("../../libraries/x86_64-linux/lib/release", dst="lib64"): + if (not self.standalone()) and self.prefix("../packages/lib/release", dst="lib64"): + self.prefix("../../libraries/x86_64-linux/lib/release", dst="lib64") self.path("libapr-1.so*") self.path("libaprutil-1.so*") - self.path("libcollada14dom.so.2.2", "libcollada14dom.so") self.path("libdb-*.so*") - self.path("libcrypto.so.*") self.path("libexpat.so*") self.path("libglod.so") - self.path("libhunspell-1.3.so*") - self.path("libminizip.so.1.2.3", "libminizip.so"); - self.path("libssl.so*") self.path("libuuid.so*") self.path("libSDL-1.2.so*") - self.path("libELFIO.so") self.path("libjpeg.so*") - self.path("libpng*.so*") - self.path("libz.so*") + self.path("libhunspell-1.3.so*") + self.path("libalut.so*") + self.path("libopenal.so*") + # Boost self.path("libboost_context-mt.so.*") @@ -953,9 +1027,13 @@ class Linux_x86_64Manifest(LinuxManifest): self.path("libboost_system-mt.so.*") self.path("libboost_thread-mt.so.*") - # OpenAL - self.path("libopenal.so*") - self.path("libalut.so*") + self.path("libcollada14dom.so.2.2", "libcollada14dom.so") + self.path("libcrypto.so.*") + self.path("libELFIO.so") + self.path("libminizip.so.1.2.3", "libminizip.so"); + self.path("libpng*.so*") + self.path("libssl.so*") + self.path("libz.so*") if 'extra_libraries' in self.args: path_list = self.args['extra_libraries'].split('|') @@ -965,26 +1043,49 @@ class Linux_x86_64Manifest(LinuxManifest): self.path(src_path, dst_path) self.end_prefix("lib64") + self.end_prefix("lib64") - # Vivox runtimes and libs - if self.prefix(src="vivox-runtime/i686-linux", dst="bin"): - self.path("SLVoice") - self.end_prefix("bin") + # Vivox runtimes and libs + if self.prefix(src="../packages/lib/release", dst="bin"): + self.path("SLVoice") + self.end_prefix("bin") - if self.prefix(src="vivox-runtime/i686-linux", dst="lib32"): - #self.path("libalut.so") - self.path("libortp.so") - self.path("libvivoxsdk.so") - self.end_prefix("lib32") + if self.prefix(src="../packages/lib/release", dst="lib32"): + #self.path("libalut.so") + self.path("libortp.so") + self.path("libvivoxsdk.so") + self.end_prefix("lib32") - # 32bit libs needed for voice - if self.prefix("../../libraries/x86_64-linux/lib/release/32bit-compat", dst="lib32"): - self.path("libalut.so") - self.path("libidn.so.11") - self.path("libopenal.so.1") - # self.path("libortp.so") - self.path("libuuid.so.1") - self.end_prefix("lib32") + # 32bit libs needed for voice + if self.prefix("../packages/lib/32bit-compat", dst="lib32"): + self.path("libalut.so") + self.path("libidn.so.11") + self.path("libopenal.so.1") + # self.path("libortp.so") + self.path("libuuid.so.1") + self.end_prefix("lib32") + +################################################################ + +def symlinkf(src, dst): + """ + Like ln -sf, but uses os.symlink() instead of running ln. + """ + try: + os.symlink(src, dst) + except OSError, err: + if err.errno != errno.EEXIST: + raise + # We could just blithely attempt to remove and recreate the target + # file, but that strategy doesn't work so well if we don't have + # permissions to remove it. Check to see if it's already the + # symlink we want, which is the usual reason for EEXIST. + if not (os.path.islink(dst) and os.readlink(dst) == src): + # Here either dst isn't a symlink or it's the wrong symlink. + # Remove and recreate. Caller will just have to deal with any + # exceptions at this stage. + os.remove(dst) + os.symlink(src, dst) if __name__ == "__main__": main() From 32706065ac73c381e7f8cdda4249959457af6f74 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Fri, 12 Dec 2014 03:49:47 -0600 Subject: [PATCH 30/55] Have CopyWinLibs only copy files needed for current configuration. --- indra/CMakeLists.txt | 2 +- indra/cmake/CopyWinLibs.cmake.in | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index d01053ba1..7ed2a7d82 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -122,7 +122,7 @@ if(WINDOWS) add_custom_command( COMMENT "Copying prebuilt libraries to viewer executable directory" OUTPUT ${CMAKE_BINARY_DIR}/CopyWinLibs - COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/CopyWinLibs.cmake + COMMAND ${CMAKE_COMMAND} -DCUR_CONFIG:STRING=${CMAKE_CFG_INTDIR} -P ${CMAKE_BINARY_DIR}/CopyWinLibs.cmake DEPENDS ${CMAKE_BINARY_DIR}/prepare/prebuilt ${CMAKE_BINARY_DIR}/CopyWinLibs.cmake ) add_custom_target(copy_win_libs DEPENDS ${CMAKE_BINARY_DIR}/CopyWinLibs) diff --git a/indra/cmake/CopyWinLibs.cmake.in b/indra/cmake/CopyWinLibs.cmake.in index 3b16a0bc7..1e7002dbe 100644 --- a/indra/cmake/CopyWinLibs.cmake.in +++ b/indra/cmake/CopyWinLibs.cmake.in @@ -23,6 +23,10 @@ set(LIBS_DEBUG_DIR ) function(copy_files paths names dest) + string(FIND ${dest} ${CUR_CONFIG} idx) + if(${idx} LESS 0) + return() + endif(${idx} LESS 0) foreach(f ${names}) foreach(p ${paths}) set(from_file "${p}/${f}") From 44f8f1776360b718c9082ceb2b0fab83ef3f4de0 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 18 Dec 2014 18:01:41 -0600 Subject: [PATCH 31/55] V3 merge for most python scripts. fmod inclusion/linkage/packaging changes. --- indra/cmake/CopyWinLibs.cmake.in | 61 -- indra/cmake/FMODEX.cmake | 141 ++-- indra/cmake/FMODSTUDIO.cmake | 143 ++-- indra/lib/python/indra/__init__.py | 27 +- indra/lib/python/indra/base/cllsd_test.py | 24 +- indra/lib/python/indra/base/lllog.py | 72 -- indra/lib/python/indra/base/llsd.py | 24 +- indra/lib/python/indra/base/lluuid.py | 15 +- indra/lib/python/indra/base/metrics.py | 94 ++- indra/lib/python/indra/ipc/servicebuilder.py | 28 +- indra/lib/python/indra/ipc/siesta.py | 132 +++- indra/lib/python/indra/util/llmanifest.py | 194 ++---- indra/lib/python/indra/util/llperformance.py | 26 +- indra/lib/python/indra/util/llsubprocess.py | 11 + indra/lib/python/indra/util/named_query.py | 58 +- .../indra/util/simperf_oprof_interface.py | 27 +- .../indra/util/simperf_proc_interface.py | 29 +- .../python/indra/util/test_win32_manifest.py | 146 +++++ indra/lib/python/uuid.py | 14 +- indra/newview/CMakeLists.txt | 137 ++-- indra/newview/viewer_manifest.py | 613 +++++++----------- install.xml | 12 +- .../automated_build_scripts/opensrc-build.sh | 472 -------------- scripts/public_fetch_tarballs.py | 280 -------- 24 files changed, 1045 insertions(+), 1735 deletions(-) delete mode 100644 indra/lib/python/indra/base/lllog.py create mode 100644 indra/lib/python/indra/util/test_win32_manifest.py delete mode 100755 scripts/automated_build_scripts/opensrc-build.sh delete mode 100755 scripts/public_fetch_tarballs.py diff --git a/indra/cmake/CopyWinLibs.cmake.in b/indra/cmake/CopyWinLibs.cmake.in index 1e7002dbe..91d5235a9 100644 --- a/indra/cmake/CopyWinLibs.cmake.in +++ b/indra/cmake/CopyWinLibs.cmake.in @@ -6,10 +6,6 @@ set(LIBS_PREBUILT_DIR "@LIBS_PREBUILT_DIR@") set(LIBS_PREBUILT_LEGACY_DIR "@LIBS_PREBUILT_LEGACY_DIR@") -set(FMODSTUDIO_SDK_DIR "@FMODSTUDIO_SDK_DIR@") -set(FMODEX_SDK_DIR "@FMODEX_SDK_DIR@") -set(FMODSTUDIO @FMODSTUDIO@) -set(FMODEX @FMODEX@) set(MSVC10 "@MSVC10@") set(WORD_SIZE "@WORD_SIZE@") @@ -181,62 +177,5 @@ if(0) copy_files("${LIBS_DEBUG_DIR}/codecs" "${plugin_codec_debug_files}" "../test_apps/llplugintest/codecs/Debug") endif(0) -if(FMODSTUDIO) - if (WORD_SIZE EQUAL 32) - set(fmodstudio_dll_file "fmod.dll") - else (WORD_SIZE EQUAL 32) - set(fmodstudio_dll_file "fmod64.dll") - endif (WORD_SIZE EQUAL 32) - - find_path(FMODSTUDIO_BINARY_RELEASE_DIR "${fmodstudio_dll_file}" - ${LIBS_RELEASE_DIR} - "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib" - "${FMODSTUDIO_SDK_DIR}" - NO_DEFAULT_PATH - ) - find_path(FMODSTUDIO_BINARY_DEBUG_DIR "${fmodstudio_dll_file}" - ${LIBS_DEBUG_DIR} - "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib" - "${FMODSTUDIO_SDK_DIR}" - NO_DEFAULT_PATH - ) - - if(FMODSTUDIO_BINARY_RELEASE_DIR) - copy_files(${FMODSTUDIO_BINARY_RELEASE_DIR} ${fmodstudio_dll_file} "Release") - copy_files(${FMODSTUDIO_BINARY_RELEASE_DIR} ${fmodstudio_dll_file} "RelWithDebInfo") - endif(FMODSTUDIO_BINARY_RELEASE_DIR) - if(FMODSTUDIO_BINARY_DEBUG_DIR) - copy_files(${FMODSTUDIO_BINARY_DEBUG_DIR} ${fmodstudio_dll_file} "Debug") - endif(FMODSTUDIO_BINARY_DEBUG_DIR) -endif(FMODSTUDIO) - -if(FMODEX) - if (WORD_SIZE EQUAL 32) - set(fmodex_dll_file "fmodex.dll") - else (WORD_SIZE EQUAL 32) - set(fmodex_dll_file "fmodex64.dll") - endif (WORD_SIZE EQUAL 32) - - find_path(FMODEX_BINARY_RELEASE_DIR "${fmodex_dll_file}" - ${LIBS_RELEASE_DIR} - "${FMODEX_SDK_DIR}/api" - "${FMODEX_SDK_DIR}" - NO_DEFAULT_PATH - ) - find_path(FMODEX_BINARY_DEBUG_DIR "${fmodex_dll_file}" - ${LIBS_DEBUG_DIR} - "${FMODSTUDIO_SDK_DIR}/api" - "${FMODSTUDIO_SDK_DIR}" - NO_DEFAULT_PATH - ) - - if(FMODEX_BINARY_RELEASE_DIR) - copy_files(${FMODEX_BINARY_RELEASE_DIR ${fmodex_dll_file} "Release") - copy_files(${FMODEX_BINARY_RELEASE_DIR ${fmodex_dll_file} "RelWithDebInfo") - endif(FMODEX_BINARY_RELEASE_DIR) - if(FMODEX_BINARY_DEBUG_DIR) - copy_files(${FMODEX_BINARY_DEBUG_DIR ${fmodex_dll_file} "Debug") - endif(FMODEX_BINARY_DEBUG_DIR) -endif(FMODEX) diff --git a/indra/cmake/FMODEX.cmake b/indra/cmake/FMODEX.cmake index acd419e9e..b38f654ee 100644 --- a/indra/cmake/FMODEX.cmake +++ b/indra/cmake/FMODEX.cmake @@ -6,81 +6,80 @@ if (FMODEX AND FMODSTUDIO) message( FATAL_ERROR "You can not enable two FMOD variants at the same time." ) endif (FMODEX AND FMODSTUDIO) -if (NOT FMODEX_LIBRARY) +unset(FMOD_LIBRARY_RELEASE CACHE) +unset(FMOD_LIBRARY_DEBUG CACHE) +unset(FMOD_LINK_LIBRARY_RELEASE CACHE) +unset(FMOD_LINK_LIBRARY_DEBUG CACHE) +unset(FMOD_INCLUDE_DIR CACHE) + +if (NOT FMODEX_SDK_DIR) set(FMODEX_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.") - if (FMODEX_SDK_DIR) - if(WORD_SIZE EQUAL 32) - find_library(FMODEX_LIBRARY - fmodex_vc fmodexL_vc fmodex fmodexL - PATHS - "${FMODEX_SDK_DIR}/api/lib" - "${FMODEX_SDK_DIR}/api" - "${FMODEX_SDK_DIR}/lib" - "${FMODEX_SDK_DIR}" - ) - elseif(WORD_SIZE EQUAL 64) - find_library(FMODEX_LIBRARY - fmodex64_vc fmodexL64_vc fmodex64 fmodexL64 - PATHS - "${FMODEX_SDK_DIR}/api/lib" - "${FMODEX_SDK_DIR}/api" - "${FMODEX_SDK_DIR}/lib" - "${FMODEX_SDK_DIR}" - ) - endif(WORD_SIZE EQUAL 32) - endif(FMODEX_SDK_DIR) - if(WINDOWS AND NOT FMODEX_SDK_DIR) - GET_FILENAME_COMPONENT(FMODEX_PROG_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Programmers\ API\ Windows] ABSOLUTE CACHE) - if(WORD_SIZE EQUAL 32) - find_library(FMODEX_LIBRARY - fmodex_vc fmodexL_vc - PATHS - "${FMODEX_PROG_DIR}/api/lib" - "${FMODEX_PROG_DIR}/api" - "${FMODEX_PROG_DIR}" - ) - else(WORD_SIZE EQUAL 32) - find_library(FMODEX_LIBRARY - fmodex64_vc fmodexL64_vc - PATHS - "${FMODEX_PROG_DIR}/api/lib" - "${FMODEX_PROG_DIR}/api" - "${FMODEX_PROG_DIR}" - ) - endif(WORD_SIZE EQUAL 32) - if(FMODEX_LIBRARY) - message(STATUS "Found fmodex in ${FMODEX_PROG_DIR}") - set(FMODEX_SDK_DIR "${FMODEX_PROG_DIR}") - set(FMODEX_SDK_DIR "${FMODEX_PROG_DIR}" CACHE PATH "Path to the FMOD Ex SDK." FORCE) - endif(FMODEX_LIBRARY) - endif(WINDOWS AND NOT FMODEX_SDK_DIR) -endif (NOT FMODEX_LIBRARY) + if(WINDOWS) + GET_FILENAME_COMPONENT(FMODEX_SDK_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Programmers\ API\ Windows] ABSOLUTE CACHE) + endif(WINDOWS) +endif (NOT FMODEX_SDK_DIR) -find_path(FMODEX_INCLUDE_DIR fmod.hpp - ${LIBS_PREBUILT_DIR}/include/fmodex - ${LIBS_PREBUILT_LEGACY_DIR}/include/fmodex - "${FMODEX_SDK_DIR}/api/inc" - "${FMODEX_SDK_DIR}/inc" - "${FMODEX_SDK_DIR}" - ) +set(release_fmod_lib_paths + ${LIBS_PREBUILT_DIR}/release/lib/ + ${LIBS_PREBUILT_LEGACY_DIR}/release/lib) +set(debug_fmod_lib_paths + ${LIBS_PREBUILT_DIR}/debug/lib + ${LIBS_PREBUILT_LEGACY_DIR}/debug/lib) +set(fmod_inc_paths + ${LIBS_PREBUILT_DIR}/include/fmodsex + ${LIBS_PREBUILT_LEGACY_DIR}/include/fmodex) -if(DARWIN) - set(FMODEX_ORIG_LIBRARY "${FMODEX_LIBRARY}") - set(FMODEX_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/libfmodex.dylib") -endif(DARWIN) -if (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR) - set(FMODEX ON CACHE BOOL "Use closed source FMOD Ex sound library.") -else (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR) - set(FMODEX_LIBRARY "") - set(FMODEX_INCLUDE_DIR "") - if (FMODEX) + +if (FMODEX_SDK_DIR) + set(release_fmod_lib_paths ${release_fmod_lib_paths} "${FMODEX_SDK_DIR}/api" "${FMODEX_SDK_DIR}/api/lib") + set(debug_fmod_lib_paths ${debug_fmod_lib_paths} "${FMODEX_SDK_DIR}/api" "${FMODEX_SDK_DIR}/api/lib") + set(fmod_inc_paths ${fmod_inc_paths} "${FMODEX_SDK_DIR}/api/inc") +endif (FMODEX_SDK_DIR) + +if(WINDOWS) + set(CMAKE_FIND_LIBRARY_SUFFIXES_OLD ${CMAKE_FIND_LIBRARY_SUFFIXES}) + set(CMAKE_FIND_LIBRARY_SUFFIXES .dll) +endif(WINDOWS) +if(WORD_SIZE EQUAL 32) #Check if CMAKE_FIND_LIBRARY_PREFIXES is set to 'lib' for darwin. + find_library(FMOD_LIBRARY_RELEASE fmodex PATHS ${release_fmod_lib_paths}) + find_library(FMOD_LIBRARY_DEBUG fmodexL PATHS ${debug_fmod_lib_paths}) +elseif(WORD_SIZE EQUAL 64) + find_library(FMOD_LIBRARY_RELEASE fmodex64 PATHS ${release_fmod_lib_paths}) + find_library(FMOD_LIBRARY_DEBUG fmodLex64 PATHS ${debug_fmod_lib_paths}) +endif (WORD_SIZE EQUAL 32) +if(WINDOWS) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_OLD}) + if(WORD_SIZE EQUAL 32) + find_library(FMOD_LINK_LIBRARY_RELEASE fmodex_vc PATHS ${release_fmod_lib_paths}) + find_library(FMOD_LINK_LIBRARY_DEBUG fmodexL_vc PATHS ${debug_fmod_lib_paths}) + elseif(WORD_SIZE EQUAL 64) + find_library(FMOD_LINK_LIBRARY_RELEASE fmodex64_vc PATHS ${release_fmod_lib_paths}) + find_library(FMOD_LINK_LIBRARY_DEBUG fmodLex64_vc PATHS ${debug_fmod_lib_paths}) + endif (WORD_SIZE EQUAL 32) +else(WINDOWS) + set(FMODSTUDIO_LINK_LIBRARY_RELEASE ${FMODSTUDIO_LIBRARY_RELEASE}) + set(FMODSTUDIO_LINK_LIBRARY_DEBUG ${FMODSTUDIO_LIBRARY_DEBUG}) +endif(WINDOWS) +find_path(FMOD_INCLUDE_DIR fmod.hpp ${fmod_inc_paths}) + +if (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) + set(FMOD ON CACHE BOOL "Use closed source FMOD sound library.") + if (NOT FMOD_LIBRARY_DEBUG) #Use release library in debug configuration if debug library is absent. + set(FMOD_LIBRARY_DEBUG ${FMOD_LIBRARY_RELEASE}) + endif (NOT FMOD_LIBRARY_DEBUG) +else (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) + unset(FMOD_LIBRARY_RELEASE CACHE) + unset(FMOD_LIBRARY_DEBUG CACHE) + unset(FMOD_INCLUDE_DIR CACHE) + if (FMOD) message(STATUS "No support for FMOD Ex audio (need to set FMODEX_SDK_DIR?)") - endif (FMODEX) - set(FMODEX OFF CACHE BOOL "Use closed source FMOD Ex sound library.") + endif (FMOD) + set(FMOD OFF CACHE BOOL "Use closed source FMOD sound library.") + set(FMOD OFF) set(FMODEX OFF) -endif (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR) +endif (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) -if (FMODEX) - message(STATUS "Building with FMOD Ex audio support") -endif (FMODEX) +if (FMOD) + message(STATUS "Building with FMOD audio support") +endif (FMOD) diff --git a/indra/cmake/FMODSTUDIO.cmake b/indra/cmake/FMODSTUDIO.cmake index ef8f65253..2b0daabfe 100644 --- a/indra/cmake/FMODSTUDIO.cmake +++ b/indra/cmake/FMODSTUDIO.cmake @@ -6,78 +6,83 @@ if (FMODEX AND FMODSTUDIO) message( FATAL_ERROR "You can not enable two FMOD variants at the same time." ) endif (FMODEX AND FMODSTUDIO) -if (NOT FMODSTUDIO_LIBRARY) - set(FMODSTUDIO_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.") - if (FMODSTUDIO_SDK_DIR) - if(WORD_SIZE EQUAL 32) - find_library(FMODSTUDIO_LIBRARY - fmod_vc fmodL_vc fmod fmodL - PATHS - "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib" - "${FMODSTUDIO_SDK_DIR}/api/lowlevel" - "${FMODSTUDIO_SDK_DIR}" - ) - elseif(WORD_SIZE EQUAL 64) - find_library(FMODSTUDIO_LIBRARY - fmod64_vc fmodL64_vc fmod64 fmodL64 - PATHS - "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib" - "${FMODSTUDIO_SDK_DIR}/api/lowlevel" - "${FMODSTUDIO_SDK_DIR}" - ) - endif(WORD_SIZE EQUAL 32) - endif(FMODSTUDIO_SDK_DIR) - if(WINDOWS AND NOT FMODSTUDIO_SDK_DIR) - GET_FILENAME_COMPONENT(FMODSTUDIO_PROG_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Studio\ API\ Windows] ABSOLUTE CACHE) - if(WORD_SIZE EQUAL 32) - find_library(FMODSTUDIO_LIBRARY - fmod_vc fmodL_vc - PATHS - "${FMODSTUDIO_PROG_DIR}/api/lowlevel/lib" - "${FMODSTUDIO_PROG_DIR}/api/lowlevel" - "${FMODSTUDIO_PROG_DIR}" - ) - else(WORD_SIZE EQUAL 32) - find_library(FMODSTUDIO_LIBRARY - fmod64_vc fmodL64_vc - PATHS - "${FMODSTUDIO_PROG_DIR}/api/lowlevel/lib" - "${FMODSTUDIO_PROG_DIR}/api/lowlevel" - "${FMODSTUDIO_PROG_DIR}" - ) - endif(WORD_SIZE EQUAL 32) - if(FMODSTUDIO_LIBRARY) - message(STATUS "Found fmodstudio in ${FMODSTUDIO_PROG_DIR}") - set(FMODSTUDIO_SDK_DIR "${FMODSTUDIO_PROG_DIR}") - set(FMODSTUDIO_SDK_DIR "${FMODSTUDIO_PROG_DIR}" CACHE PATH "Path to the FMOD Studio SDK." FORCE) - endif(FMODSTUDIO_LIBRARY) - endif(WINDOWS AND NOT FMODSTUDIO_SDK_DIR) -endif (NOT FMODSTUDIO_LIBRARY) +unset(FMOD_LIBRARY_RELEASE CACHE) +unset(FMOD_LIBRARY_DEBUG CACHE) +unset(FMOD_LINK_LIBRARY_RELEASE CACHE) +unset(FMOD_LINK_LIBRARY_DEBUG CACHE) +unset(FMOD_INCLUDE_DIR CACHE) -find_path(FMODSTUDIO_INCLUDE_DIR fmod.hpp - ${LIBS_PREBUILT_DIR}/include/fmodstudio - ${LIBS_PREBUILT_LEGACY_DIR}/include/fmodstudio - "${FMODSTUDIO_SDK_DIR}/api/lowlevel/inc" - "${FMODSTUDIO_SDK_DIR}" - ) +if (NOT FMODSTUDIO_SDK_DIR) + set(FMODSTUDIO_SDK_DIR CACHE PATH "Path to the FMOD Studio SDK.") + if(WINDOWS) + GET_FILENAME_COMPONENT(FMODSTUDIO_SDK_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Studio\ API\ Windows] ABSOLUTE CACHE) + endif(WINDOWS) +endif (NOT FMODSTUDIO_SDK_DIR) -if(DARWIN) - set(FMODSTUDIO_ORIG_LIBRARY "${FMODSTUDIO_LIBRARY}") - set(FMODSTUDIO_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/libfmod.dylib") -endif(DARWIN) +set(release_fmod_lib_paths + ${LIBS_PREBUILT_DIR}/release/lib/ + ${LIBS_PREBUILT_LEGACY_DIR}/release/lib) +set(debug_fmod_lib_paths + ${LIBS_PREBUILT_DIR}/debug/lib + ${LIBS_PREBUILT_LEGACY_DIR}/debug/lib) +set(fmod_inc_paths + ${LIBS_PREBUILT_DIR}/include/fmodstudio + ${LIBS_PREBUILT_LEGACY_DIR}/include/fmodstudio) -if (FMODSTUDIO_LIBRARY AND FMODSTUDIO_INCLUDE_DIR) - set(FMODSTUDIO ON CACHE BOOL "Use closed source FMOD Studio sound library.") -else (FMODSTUDIO_LIBRARY AND FMODSTUDIO_INCLUDE_DIR) - set(FMODSTUDIO_LIBRARY "") - set(FMODSTUDIO_INCLUDE_DIR "") - if (FMODSTUDIO) + + +if (FMODSTUDIO_SDK_DIR) + if(LINUX AND WORD_SIZE EQUAL 32) + set(release_lib_paths ${release_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/x86/lib" ) + set(debug__lib_paths ${debug_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/x86/lib") + elseif(LINUX) + set(release__lib_paths ${release_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/x86_64/lib") + set(debug_fmod_lib_paths ${debug_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/x86_64/lib") + else(LINUX AND WORD_SIZE EQUAL 32) + set(release_fmod_lib_paths ${release_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib") + set(debug_fmod_lib_paths ${debug_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib") + endif(LINUX AND WORD_SIZE EQUAL 32) + set(fmod_inc_paths ${fmod_inc_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/inc") +endif (FMODSTUDIO_SDK_DIR) + +if(WINDOWS) + set(CMAKE_FIND_LIBRARY_SUFFIXES_OLD ${CMAKE_FIND_LIBRARY_SUFFIXES}) + set(CMAKE_FIND_LIBRARY_SUFFIXES .dll) +endif(WINDOWS) +if(WORD_SIZE EQUAL 32) #Check if CMAKE_FIND_LIBRARY_PREFIXES is set to 'lib' for darwin. + find_library(FMOD_LIBRARY_RELEASE fmod PATHS ${release_fmod_lib_paths}) + find_library(FMOD_LIBRARY_DEBUG fmodL PATHS ${debug_fmod_lib_paths}) +elseif(WORD_SIZE EQUAL 64) + find_library(FMOD_LIBRARY_RELEASE fmod64 PATHS ${release_fmod_lib_paths}) + find_library(FMOD_LIBRARY_DEBUG fmodL64 PATHS ${debug_fmod_lib_paths}) +endif (WORD_SIZE EQUAL 32) +if(WINDOWS) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_OLD}) + string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) + string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) +else(WINDOWS) + set(FMODSTUDIO_LINK_LIBRARY_RELEASE ${FMODSTUDIO_LIBRARY_RELEASE}) + set(FMODSTUDIO_LINK_LIBRARY_DEBUG ${FMODSTUDIO_LIBRARY_DEBUG}) +endif(WINDOWS) +find_path(FMOD_INCLUDE_DIR fmod.hpp ${fmod_inc_paths}) + +if (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) + set(FMOD ON CACHE BOOL "Use closed source FMOD sound library.") + if (NOT FMOD_LIBRARY_DEBUG) #Use release library in debug configuration if debug library is absent. + set(FMOD_LIBRARY_DEBUG ${FMOD_LIBRARY_RELEASE}) + endif (NOT FMOD_LIBRARY_DEBUG) +else (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) + unset(FMOD_LIBRARY_RELEASE CACHE) + unset(FMOD_LIBRARY_DEBUG CACHE) + unset(FMOD_INCLUDE_DIR CACHE) + if (FMOD) message(STATUS "No support for FMOD Studio audio (need to set FMODSTUDIO_SDK_DIR?)") - endif (FMODSTUDIO) - set(FMODSTUDIO OFF CACHE BOOL "Use closed source FMOD Studio sound library.") + endif (FMOD) + set(FMOD OFF CACHE BOOL "Use closed source FMOD sound library.") + set(FMOD OFF) set(FMODSTUDIO OFF) -endif (FMODSTUDIO_LIBRARY AND FMODSTUDIO_INCLUDE_DIR) +endif (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) -if (FMODSTUDIO) - message(STATUS "Building with FMOD Studio audio support") -endif (FMODSTUDIO) +if (FMOD) + message(STATUS "Building with FMOD audio support") +endif (FMOD) diff --git a/indra/lib/python/indra/__init__.py b/indra/lib/python/indra/__init__.py index 9daab3480..0c5053cf4 100644 --- a/indra/lib/python/indra/__init__.py +++ b/indra/lib/python/indra/__init__.py @@ -2,19 +2,24 @@ @file __init__.py @brief Initialization file for the indra module. -$LicenseInfo:firstyear=2006&license=internal$ +$LicenseInfo:firstyear=2006&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2006-2010, Linden Research, Inc. -Copyright (c) 2006-2009, Linden Research, Inc. +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. -The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -this source code is governed by the Linden Lab Source Code Disclosure -Agreement ("Agreement") previously entered between you and Linden -Lab. By accessing, using, copying, modifying or distributing this -software, you acknowledge that you have been informed of your -obligations under the Agreement and agree to abide by those obligations. +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. -ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -COMPLETENESS OR PERFORMANCE. +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$ """ diff --git a/indra/lib/python/indra/base/cllsd_test.py b/indra/lib/python/indra/base/cllsd_test.py index 3af59e741..1f06898ff 100644 --- a/indra/lib/python/indra/base/cllsd_test.py +++ b/indra/lib/python/indra/base/cllsd_test.py @@ -1,3 +1,25 @@ +#!/usr/bin/python +## +## $LicenseInfo:firstyear=2011&license=viewerlgpl$ +## Second Life Viewer Source Code +## Copyright (C) 2011, Linden Research, Inc. +## +## This library is free software; you can redistribute it and/or +## modify it under the terms of the GNU Lesser General Public +## License as published by the Free Software Foundation; +## version 2.1 of the License only. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public +## License along with this library; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +## +## Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +## $/LicenseInfo$ from indra.base import llsd, lluuid from datetime import datetime import cllsd @@ -10,7 +32,7 @@ values = ( '&<>', u'\u81acj', llsd.uri('http://foo<'), - lluuid.LLUUID(), + lluuid.UUID(), llsd.LLSD(['thing']), 1, myint(31337), diff --git a/indra/lib/python/indra/base/lllog.py b/indra/lib/python/indra/base/lllog.py deleted file mode 100644 index 31000fcb9..000000000 --- a/indra/lib/python/indra/base/lllog.py +++ /dev/null @@ -1,72 +0,0 @@ -"""\ -@file lllog.py -@brief Logging for event processing - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -from indra.base.llsd import format_notation - -try: - import syslog -except ImportError: - # Windows - import sys - - class syslog(object): - _logfp = sys.stderr - - def syslog(msg): - _logfp.write(msg) - if not msg.endswith('\n'): - _logfp.write('\n') - - syslog = staticmethod(syslog) - -class Logger(object): - def __init__(self, name='indra'): - self._sequence = 0 - try: - syslog.openlog(name, syslog.LOG_CONS | syslog.LOG_PID, - syslog.LOG_LOCAL0) - except AttributeError: - # No syslog module on Windows - pass - - def next(self): - self._sequence += 1 - return self._sequence - - def log(self, msg, llsd): - payload = 'INFO: log: LLLOGMESSAGE (%d) %s %s' % (self.next(), msg, - format_notation(llsd)) - syslog.syslog(payload) - -_logger = None - -def log(msg, llsd): - global _logger - if _logger is None: - _logger = Logger() - _logger.log(msg, llsd) diff --git a/indra/lib/python/indra/base/llsd.py b/indra/lib/python/indra/base/llsd.py index 9534d5935..4527b115f 100644 --- a/indra/lib/python/indra/base/llsd.py +++ b/indra/lib/python/indra/base/llsd.py @@ -72,8 +72,11 @@ BOOL_FALSE = ('0', '0.0', 'false', '') def format_datestr(v): - """ Formats a datetime object into the string format shared by xml and notation serializations.""" - return v.isoformat() + 'Z' + """ Formats a datetime or date object into the string format shared by xml and notation serializations.""" + if hasattr(v, 'microsecond'): + return v.isoformat() + 'Z' + else: + return v.strftime('%Y-%m-%dT%H:%M:%SZ') def parse_datestr(datestr): """Parses a datetime object from the string format shared by xml and notation serializations.""" @@ -183,6 +186,7 @@ class LLSDXMLFormatter(object): unicode : self.STRING, uri : self.URI, datetime.datetime : self.DATE, + datetime.date : self.DATE, list : self.ARRAY, tuple : self.ARRAY, types.GeneratorType : self.ARRAY, @@ -234,7 +238,7 @@ class LLSDXMLFormatter(object): def MAP(self, v): return self.elt( 'map', - ''.join(["%s%s" % (self.elt('key', key), self.generate(value)) + ''.join(["%s%s" % (self.elt('key', self.xml_esc(str(key))), self.generate(value)) for key, value in v.items()])) typeof = type @@ -347,6 +351,7 @@ class LLSDNotationFormatter(object): unicode : self.STRING, uri : self.URI, datetime.datetime : self.DATE, + datetime.date : self.DATE, list : self.ARRAY, tuple : self.ARRAY, types.GeneratorType : self.ARRAY, @@ -924,12 +929,13 @@ def _format_binary_recurse(something): (type(something), something)) -def parse_binary(something): - header = '\n' - if not something.startswith(header): - raise LLSDParseError('LLSD binary encoding header not found') - return LLSDBinaryParser().parse(something[len(header):]) - +def parse_binary(binary): + if binary.startswith(''): + just_binary = binary.split('\n', 1)[1] + else: + just_binary = binary + return LLSDBinaryParser().parse(just_binary) + def parse_xml(something): try: return to_python(fromstring(something)[0]) diff --git a/indra/lib/python/indra/base/lluuid.py b/indra/lib/python/indra/base/lluuid.py index cff190087..7413ffe10 100644 --- a/indra/lib/python/indra/base/lluuid.py +++ b/indra/lib/python/indra/base/lluuid.py @@ -28,13 +28,12 @@ $/LicenseInfo$ import random, socket, string, time, re import uuid - -# *HACK: Necessary for python 2.4. Consider replacing this code wart -# after python >=2.5 has deployed everywhere. 2009-10-05 try: + # Python 2.6 from hashlib import md5 except ImportError: - from md5 import md5 + # Python 2.5 and earlier + from md5 import new as md5 def _int2binstr(i,l): s='' @@ -73,7 +72,7 @@ class UUID(object): ip = '' try: ip = socket.gethostbyname(socket.gethostname()) - except(socket.gaierror): + except(socket.gaierror, socket.error): # no ip address, so just default to somewhere in 10.x.x.x ip = '10' for i in range(3): @@ -164,7 +163,7 @@ class UUID(object): def setFromMemoryDump(self, gdb_string): """ We expect to get gdb_string as four hex units. eg: - 0x147d54db 0xc34b3f1b 0x714f989b 0x0a892fd2 + 0x147d54db 0xc34b3f1b 0x714f989b 0x0a892fd2 Which will be translated to: db547d14-1b3f4bc3-9b984f71-d22f890a Returns self. @@ -188,7 +187,7 @@ class UUID(object): def getAsString(self): """ Return a different string representation of the form - AAAAAAAA-AAAABBBB-BBBBBBBB-BBCCCCCC (a 128-bit number in hex) + AAAAAAAA-AAAABBBB-BBBBBBBB-BBCCCCCC (a 128-bit number in hex) where A=network address, B=timestamp, C=random. """ i1 = _binstr2int(self._bits[0:4]) @@ -234,7 +233,7 @@ NULL = UUID() def printTranslatedMemory(four_hex_uints): """ We expect to get the string as four hex units. eg: - 0x147d54db 0xc34b3f1b 0x714f989b 0x0a892fd2 + 0x147d54db 0xc34b3f1b 0x714f989b 0x0a892fd2 Which will be translated to: db547d14-1b3f4bc3-9b984f71-d22f890a """ diff --git a/indra/lib/python/indra/base/metrics.py b/indra/lib/python/indra/base/metrics.py index 8f2a85cf0..ff8380265 100644 --- a/indra/lib/python/indra/base/metrics.py +++ b/indra/lib/python/indra/base/metrics.py @@ -29,25 +29,93 @@ $/LicenseInfo$ """ import sys -from indra.base import llsd +try: + import syslog +except ImportError: + # Windows + import sys + class syslog(object): + # wrap to a lame syslog for windows + _logfp = sys.stderr + def syslog(msg): + _logfp.write(msg) + if not msg.endswith('\n'): + _logfp.write('\n') + syslog = staticmethod(syslog) -_sequence_id = 0 +from indra.base.llsd import format_notation -def record_metrics(table, stats, dest=None): +def record_metrics(table, stats): "Write a standard metrics log" - _log("LLMETRICS", table, stats, dest) + _log("LLMETRICS", table, stats) -def record_event(table, data, dest=None): +def record_event(table, data): "Write a standard logmessage log" - _log("LLLOGMESSAGE", table, data, dest) + _log("LLLOGMESSAGE", table, data) -def _log(header, table, data, dest): +def set_destination(dest): + """Set the destination of metrics logs for this process. + + If you do not call this function prior to calling a logging + method, that function will open sys.stdout as a destination. + Attempts to set dest to None will throw a RuntimeError. + @param dest a file-like object which will be the destination for logs.""" if dest is None: - # do this check here in case sys.stdout changes at some - # point. as a default parameter, it will never be - # re-evaluated. - dest = sys.stdout + raise RuntimeError("Attempt to unset metrics destination.") + global _destination + _destination = dest + +def destination(): + """Get the destination of the metrics logs for this process. + Returns None if no destination is set""" + global _destination + return _destination + +class SysLogger(object): + "A file-like object which writes to syslog." + def __init__(self, ident='indra', logopt = None, facility = None): + try: + if logopt is None: + logopt = syslog.LOG_CONS | syslog.LOG_PID + if facility is None: + facility = syslog.LOG_LOCAL0 + syslog.openlog(ident, logopt, facility) + import atexit + atexit.register(syslog.closelog) + except AttributeError: + # No syslog module on Windows + pass + + def write(str): + syslog.syslog(str) + write = staticmethod(write) + + def flush(): + pass + flush = staticmethod(flush) + +# +# internal API +# +_sequence_id = 0 +_destination = None + +def _next_id(): global _sequence_id - print >>dest, header, "(" + str(_sequence_id) + ")", - print >>dest, table, llsd.format_notation(data) + next = _sequence_id _sequence_id += 1 + return next + +def _dest(): + global _destination + if _destination is None: + # this default behavior is documented in the metrics functions above. + _destination = sys.stdout + return _destination + +def _log(header, table, data): + log_line = "%s (%d) %s %s" \ + % (header, _next_id(), table, format_notation(data)) + dest = _dest() + dest.write(log_line) + dest.flush() diff --git a/indra/lib/python/indra/ipc/servicebuilder.py b/indra/lib/python/indra/ipc/servicebuilder.py index 04ccee765..0a0ce2b4e 100644 --- a/indra/lib/python/indra/ipc/servicebuilder.py +++ b/indra/lib/python/indra/ipc/servicebuilder.py @@ -39,6 +39,12 @@ except: pass _g_builder = None +def _builder(): + global _g_builder + if _g_builder is None: + _g_builder = ServiceBuilder() + return _g_builder + def build(name, context={}, **kwargs): """ Convenience method for using a global, singleton, service builder. Pass arguments either via a dict or via python keyword arguments, or both! @@ -56,6 +62,11 @@ def build(name, context={}, **kwargs): _g_builder = ServiceBuilder() return _g_builder.buildServiceURL(name, context, **kwargs) +def build_path(name, context={}, **kwargs): + context = context.copy() # shouldn't modify the caller's dictionary + context.update(kwargs) + return _builder().buildPath(name, context) + class ServiceBuilder(object): def __init__(self, services_definition = services_config): """\ @@ -73,12 +84,21 @@ class ServiceBuilder(object): continue if isinstance(service_builder, dict): # We will be constructing several builders - for name, builder in service_builder.items(): + for name, builder in service_builder.iteritems(): full_builder_name = service['name'] + '-' + name self.builders[full_builder_name] = builder else: self.builders[service['name']] = service_builder + def buildPath(self, name, context): + """\ + @brief given the environment on construction, return a service path. + @param name The name of the service. + @param context A dict of name value lookups for the service. + @returns Returns the + """ + return russ.format(self.builders[name], context) + def buildServiceURL(self, name, context={}, **kwargs): """\ @brief given the environment on construction, return a service URL. @@ -108,7 +128,7 @@ def on_in(query_name, host_key, schema_key): @param schema_key Logical name of destination schema. Will be looked up in indra.xml. """ - host_name = config.get(host_key) - schema_name = config.get(schema_key) - return '/'.join( ('on', host_name, 'in', schema_name, query_name.lstrip('/')) ) + return "on/config:%s/in/config:%s/%s" % (host_key.strip('/'), + schema_key.strip('/'), + query_name.lstrip('/')) diff --git a/indra/lib/python/indra/ipc/siesta.py b/indra/lib/python/indra/ipc/siesta.py index b206f181c..d867e7153 100644 --- a/indra/lib/python/indra/ipc/siesta.py +++ b/indra/lib/python/indra/ipc/siesta.py @@ -1,3 +1,32 @@ +"""\ +@file siesta.py +@brief A tiny llsd based RESTful web services framework + +$LicenseInfo:firstyear=2008&license=mit$ + +Copyright (c) 2008, Linden Research, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +$/LicenseInfo$ +""" + +from indra.base import config from indra.base import llsd from webob import exc import webob @@ -37,11 +66,11 @@ def mime_type(content_type): return content_type.split(';', 1)[0].strip().lower() class BodyLLSD(object): - '''Give a webob Request or Response an llsd property. + '''Give a webob Request or Response an llsd based "content" property. - Getting the llsd property parses the body, and caches the result. + Getting the content property parses the body, and caches the result. - Setting the llsd property formats a payload, and the body property + Setting the content property formats a payload, and the body property is set.''' def _llsd__get(self): @@ -80,7 +109,7 @@ class BodyLLSD(object): if hasattr(self, '_llsd'): del self._llsd - llsd = property(_llsd__get, _llsd__set, _llsd__del) + content = property(_llsd__get, _llsd__set, _llsd__del) class Response(webob.Response, BodyLLSD): @@ -114,10 +143,10 @@ class Request(webob.Request, BodyLLSD): Sensible content type and accept headers are used by default. - Setting the llsd property also sets the body. Getting the llsd + Setting the content property also sets the body. Getting the content property parses the body if necessary. - If you set the body property directly, the llsd property will be + If you set the body property directly, the content property will be deleted.''' default_content_type = 'application/llsd+xml' @@ -149,11 +178,11 @@ class Request(webob.Request, BodyLLSD): body = property(webob.Request._body__get, _body__set, webob.Request._body__del, webob.Request._body__get.__doc__) - def create_response(self, llsd=None, status='200 OK', + def create_response(self, content=None, status='200 OK', conditional_response=webob.NoDefault): resp = self.ResponseClass(status=status, request=self, conditional_response=conditional_response) - resp.llsd = llsd + resp.content = content return resp def curl(self): @@ -196,12 +225,18 @@ llsd_formatters = { 'application/xml': llsd.format_xml, } +formatter_qualities = ( + ('application/llsd+xml', 1.0), + ('application/llsd+notation', 0.5), + ('application/llsd+binary', 0.4), + ('application/xml', 0.3), + ('application/json', 0.2), + ) def formatter_for_mime_type(mime_type): '''Return a formatter that encodes to the given MIME type. The result is a pair of function and MIME type.''' - try: return llsd_formatters[mime_type], mime_type except KeyError: @@ -214,21 +249,19 @@ def formatter_for_request(req): '''Return a formatter that encodes to the preferred type of the client. The result is a pair of function and actual MIME type.''' - - for ctype in req.accept.best_matches('application/llsd+xml'): - try: - return llsd_formatters[ctype], ctype - except KeyError: - pass - else: + ctype = req.accept.best_match(formatter_qualities) + try: + return llsd_formatters[ctype], ctype + except KeyError: raise exc.HTTPNotAcceptable().exception def wsgi_adapter(func, environ, start_response): '''Adapt a Siesta callable to act as a WSGI application.''' - + # Process the request as appropriate. try: req = Request(environ) + #print req.urlvars resp = func(req, **req.urlvars) if not isinstance(resp, webob.Response): try: @@ -281,7 +314,8 @@ def llsd_class(cls): allowed = [m for m in http11_methods if hasattr(instance, 'handle_' + m.lower())] raise exc.HTTPMethodNotAllowed( - headers={'Allowed': ', '.join(allowed)}).exception + headers={'Allow': ', '.join(allowed)}).exception + #print "kwargs: ", kwargs return handler(req, **kwargs) def replacement(environ, start_response): @@ -336,7 +370,7 @@ def curl(reqs): route_re = re.compile(r''' \{ # exact character "{" - (\w+) # variable name (restricted to a-z, 0-9, _) + (\w*) # "config" or variable (restricted to a-z, 0-9, _) (?:([:~])([^}]+))? # optional :type or ~regex part \} # exact character "}" ''', re.VERBOSE) @@ -344,27 +378,37 @@ route_re = re.compile(r''' predefined_regexps = { 'uuid': r'[a-f0-9][a-f0-9-]{31,35}', 'int': r'\d+', + 'host': r'[a-z0-9][a-z0-9\-\.]*', } def compile_route(route): fp = StringIO() last_pos = 0 for match in route_re.finditer(route): + #print "matches: ", match.groups() fp.write(re.escape(route[last_pos:match.start()])) var_name = match.group(1) sep = match.group(2) expr = match.group(3) - if expr: - if sep == ':': - expr = predefined_regexps[expr] - # otherwise, treat what follows '~' as a regexp + if var_name == 'config': + expr = re.escape(str(config.get(var_name))) else: - expr = '[^/]+' - expr = '(?P<%s>%s)' % (var_name, expr) + if expr: + if sep == ':': + expr = predefined_regexps[expr] + # otherwise, treat what follows '~' as a regexp + else: + expr = '[^/]+' + if var_name != '': + expr = '(?P<%s>%s)' % (var_name, expr) + else: + expr = '(%s)' % (expr,) fp.write(expr) last_pos = match.end() fp.write(re.escape(route[last_pos:])) - return '^%s$' % fp.getvalue() + compiled_route = '^%s$' % fp.getvalue() + #print route, "->", compiled_route + return compiled_route class Router(object): '''WSGI routing class. Parses a URL and hands off a request to @@ -372,21 +416,43 @@ class Router(object): responds with a 404.''' def __init__(self): - self.routes = [] - self.paths = [] + self._new_routes = [] + self._routes = [] + self._paths = [] def add(self, route, app, methods=None): - self.paths.append(route) - self.routes.append((re.compile(compile_route(route)), app, - methods and dict.fromkeys(methods))) + self._new_routes.append((route, app, methods)) + + def _create_routes(self): + for route, app, methods in self._new_routes: + self._paths.append(route) + self._routes.append( + (re.compile(compile_route(route)), + app, + methods and dict.fromkeys(methods))) + self._new_routes = [] def __call__(self, environ, start_response): + # load up the config from the config file. Only needs to be + # done once per interpreter. This is the entry point of all + # siesta applications, so this is where we trap it. + _conf = config.get_config() + if _conf is None: + import os.path + fname = os.path.join( + environ.get('ll.config_dir', '/local/linden/etc'), + 'indra.xml') + config.load(fname) + + # proceed with handling the request + self._create_routes() path_info = environ['PATH_INFO'] request_method = environ['REQUEST_METHOD'] allowed = [] - for regex, app, methods in self.routes: + for regex, app, methods in self._routes: m = regex.match(path_info) if m: + #print "groupdict:",m.groupdict() if not methods or request_method in methods: environ['paste.urlvars'] = m.groupdict() return app(environ, start_response) @@ -396,7 +462,7 @@ class Router(object): allowed = dict.fromkeys(allows).keys() allowed.sort() resp = exc.HTTPMethodNotAllowed( - headers={'Allowed': ', '.join(allowed)}) + headers={'Allow': ', '.join(allowed)}) else: resp = exc.HTTPNotFound() return resp(environ, start_response) diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py index d4e325062..8b8ed825d 100644 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -41,14 +41,6 @@ import tarfile import errno import subprocess -class ManifestError(RuntimeError): - """Use an exception more specific than generic Python RuntimeError""" - pass - -class MissingError(ManifestError): - """You specified a file that doesn't exist""" - pass - def path_ancestors(path): drive, path = os.path.splitdrive(os.path.normpath(path)) result = [] @@ -253,25 +245,15 @@ def main(): for opt in args: print "Option:", opt, "=", args[opt] - # Build base package. - touch = args.get('touch') - if touch: - print 'Creating base package' - args['package_id'] = "" # base package has no package ID wm = LLManifest.for_platform(args['platform'], args.get('arch'))(args) wm.do(*args['actions']) - # Store package file for later if making touched file. - base_package_file = "" - if touch: - print 'Created base package ', wm.package_file - base_package_file = "" + wm.package_file # Write out the package file in this format, so that it can easily be called # and used in a .bat file - yeah, it sucks, but this is the simplest... touch = args.get('touch') if touch: fp = open(touch, 'w') - fp.write('set package_file=%s\n' % base_package_file) + fp.write('set package_file=%s\n' % wm.package_file) fp.close() print 'touched', touch return 0 @@ -298,13 +280,14 @@ class LLManifest(object): self.file_list = [] self.excludes = [] self.actions = [] - self.src_prefix = [args['source']] - self.artwork_prefix = [args['artwork']] - self.build_prefix = [args['build']] - self.dst_prefix = [args['dest']] + self.src_prefix = list([args['source']]) + self.artwork_prefix = list([args['artwork']]) + self.build_prefix = list([args['build']]) + self.alt_build_prefix = list([args['build']]) + self.dst_prefix = list([args['dest']]) self.created_paths = [] self.package_name = "Unknown" - + def default_grid(self): return self.args.get('grid', None) == '' def default_channel(self): @@ -328,7 +311,7 @@ class LLManifest(object): in the file list by path().""" self.excludes.append(glob) - def prefix(self, src='', build=None, dst=None): + def prefix(self, src='', build=None, dst=None, alt_build=None): """ Pushes a prefix onto the stack. Until end_prefix is called, all relevant method calls (esp. to path()) will prefix paths with the entire prefix stack. Source and destination @@ -339,10 +322,15 @@ class LLManifest(object): dst = src if build is None: build = src + if alt_build is None: + alt_build = build + self.src_prefix.append(src) self.artwork_prefix.append(src) self.build_prefix.append(build) self.dst_prefix.append(dst) + self.alt_build_prefix.append(alt_build) + return True # so that you can wrap it in an if to get indentation def end_prefix(self, descr=None): @@ -355,25 +343,30 @@ class LLManifest(object): src = self.src_prefix.pop() artwork = self.artwork_prefix.pop() build = self.build_prefix.pop() + alt_build_prefix = self.alt_build_prefix.pop() dst = self.dst_prefix.pop() if descr and not(src == descr or build == descr or dst == descr): raise ValueError, "End prefix '" + descr + "' didn't match '" +src+ "' or '" +dst + "'" def get_src_prefix(self): """ Returns the current source prefix.""" - return os.path.join(*self.src_prefix) + return os.path.relpath(os.path.normpath(os.path.join(*self.src_prefix))) def get_artwork_prefix(self): """ Returns the current artwork prefix.""" - return os.path.join(*self.artwork_prefix) + return os.path.relpath(os.path.normpath(os.path.join(*self.artwork_prefix))) def get_build_prefix(self): """ Returns the current build prefix.""" - return os.path.join(*self.build_prefix) + return os.path.relpath(os.path.normpath(os.path.join(*self.build_prefix))) + + def get_alt_build_prefix(self): + """ Returns the current alternate source prefix.""" + return os.path.relpath(os.path.normpath(os.path.join(*self.alt_build_prefix))) def get_dst_prefix(self): """ Returns the current destination prefix.""" - return os.path.join(*self.dst_prefix) + return os.path.relpath(os.path.normpath(os.path.join(*self.dst_prefix))) def src_path_of(self, relpath): """Returns the full path to a file or directory specified @@ -390,26 +383,10 @@ class LLManifest(object): relative to the destination directory.""" return os.path.join(self.get_dst_prefix(), relpath) - def ensure_src_dir(self, reldir): - """Construct the path for a directory relative to the - source path, and ensures that it exists. Returns the - full path.""" - path = os.path.join(self.get_src_prefix(), reldir) - self.cmakedirs(path) - return path - - def ensure_dst_dir(self, reldir): - """Construct the path for a directory relative to the - destination path, and ensures that it exists. Returns the - full path.""" - path = os.path.join(self.get_dst_prefix(), reldir) - self.cmakedirs(path) - return path - def run_command(self, command): """ Runs an external command, and returns the output. Raises - an exception if the command returns a nonzero status code. For - debugging/informational purposes, prints out the command's + an exception if the command reurns a nonzero status code. For + debugging/informational purpoases, prints out the command's output as it is received.""" print "Running command:", command fd = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) @@ -422,7 +399,7 @@ class LLManifest(object): print lines[-1].rstrip('\n'), output = ''.join(lines) if fd.returncode: - raise ManifestError( + raise RuntimeError( "Command %s returned non-zero status (%s) \noutput:\n%s" % (command, fd.returncode, output) ) return output @@ -432,24 +409,14 @@ class LLManifest(object): a) verify that you really have created it b) schedule it for cleanup""" if not os.path.exists(path): - raise ManifestError, "Should be something at path " + path + raise RuntimeError, "Should be something at path " + path self.created_paths.append(path) - def put_in_file(self, contents, dst, src=None): + def put_in_file(self, contents, dst): # write contents as dst - dst_path = self.dst_path_of(dst) - f = open(dst_path, "wb") - try: - f.write(contents) - finally: - f.close() - - # Why would we create a file in the destination tree if not to include - # it in the installer? The default src=None (plus the fact that the - # src param is last) is to preserve backwards compatibility. - if src: - self.file_list.append([src, dst_path]) - return dst_path + f = open(self.dst_path_of(dst), "wb") + f.write(contents) + f.close() def replace_in(self, src, dst=None, searchdict={}): if dst == None: @@ -512,29 +479,30 @@ class LLManifest(object): if method is not None: method(src, dst) self.file_list.append([src, dst]) - return 1 + return [dst] else: sys.stdout.write(" (excluding %r, %r)" % (src, dst)) sys.stdout.flush() - return 0 + return [] def process_directory(self, src, dst): if not self.includes(src, dst): sys.stdout.write(" (excluding %r, %r)" % (src, dst)) sys.stdout.flush() - return 0 + return [] names = os.listdir(src) self.cmakedirs(dst) errors = [] + found_files = [] count = 0 for name in names: srcname = os.path.join(src, name) dstname = os.path.join(dst, name) if os.path.isdir(srcname): - count += self.process_directory(srcname, dstname) + found_files.extend(self.process_directory(srcname, dstname)) else: - count += self.process_file(srcname, dstname) - return count + found_files.extend(self.process_file(srcname, dstname)) + return found_files def includes(self, src, dst): if src: @@ -597,7 +565,7 @@ class LLManifest(object): except (IOError, os.error), why: errors.append((srcname, dstname, why)) if errors: - raise ManifestError, errors + raise RuntimeError, errors def cmakedirs(self, path): @@ -614,25 +582,11 @@ class LLManifest(object): if os.path.exists(f): return f # didn't find it, return last item in list - if len(list) > 0: + if list: return list[-1] else: return None - def contents_of_tar(self, src_tar, dst_dir): - """ Extracts the contents of the tarfile (specified - relative to the source prefix) into the directory - specified relative to the destination directory.""" - self.check_file_exists(src_tar) - tf = tarfile.open(self.src_path_of(src_tar), 'r') - for member in tf.getmembers(): - tf.extract(member, self.ensure_dst_dir(dst_dir)) - # TODO get actions working on these dudes, perhaps we should extract to a temporary directory and then process_directory on it? - self.file_list.append([src_tar, - self.dst_path_of(os.path.join(dst_dir,member.name))]) - tf.close() - - def wildcard_regex(self, src_glob, dst_glob): src_re = re.escape(src_glob) src_re = src_re.replace('\*', '([-a-zA-Z0-9._ ]*)') @@ -643,12 +597,7 @@ class LLManifest(object): i = i+1 return re.compile(src_re), dst_temp - def check_file_exists(self, path): - if not os.path.exists(path) and not os.path.islink(path): - raise MissingError("Path %s doesn't exist" % (os.path.abspath(path),)) - - - wildcard_pattern = re.compile(r'\*') + wildcard_pattern = re.compile('\*') def expand_globs(self, src, dst): src_list = glob.glob(src) src_re, d_template = self.wildcard_regex(src.replace('\\', '/'), @@ -675,53 +624,44 @@ class LLManifest(object): return self.path(os.path.join(path, file), file) def path(self, src, dst=None): - sys.stdout.write("Processing %s => %s ... " % (src, dst)) sys.stdout.flush() if src == None: - raise ManifestError("No source file, dst is " + dst) + raise RuntimeError("No source file, dst is " + dst) if dst == None: dst = src dst = os.path.join(self.get_dst_prefix(), dst) + sys.stdout.write("Processing %s => %s ... " % (src, dst)) + count = 0 + is_glob = False + found_files = [] - def try_path(src): - # expand globs - count = 0 - if self.wildcard_pattern.search(src): - for s,d in self.expand_globs(src, dst): + # look under each prefix for matching paths. Paths are normalized so './../blah' will match '../blah/../blah/' + paths = set([os.path.normpath(os.path.join(self.get_src_prefix(), src)), + os.path.normpath(os.path.join(self.get_artwork_prefix(), src)), + os.path.normpath(os.path.join(self.get_build_prefix(), src)), + os.path.normpath(os.path.join(self.get_alt_build_prefix(), src))] + ) + for path in paths: + print path + if self.wildcard_pattern.search(path): + is_glob = True + for s,d in self.expand_globs(path, dst): assert(s != d) - count += self.process_file(s, d) + found_files.extend(self.process_file(s, d)) else: - # if we're specifying a single path (not a glob), - # we should error out if it doesn't exist - self.check_file_exists(src) # if it's a directory, recurse through it - if os.path.isdir(src): - count += self.process_directory(src, dst) - else: - count += self.process_file(src, dst) - return count + if os.path.isdir(path): + found_files.extend(self.process_directory(path, dst)) + elif os.path.exists(path): + found_files.extend(self.process_file(path, dst)) - for pfx in self.get_src_prefix(), self.get_artwork_prefix(), self.get_build_prefix(): - try: - count = try_path(os.path.join(pfx, src)) - except MissingError: - # If src isn't a wildcard, and if that file doesn't exist in - # this pfx, try next pfx. - count = 0 - continue + # if we're specifying a single path (not a glob), + # we should error out if it doesn't exist + if not found_files and not is_glob: + raise RuntimeError("No files match %s\n" % str(paths)) - # Here try_path() didn't raise MissingError. Did it process any files? - if count: - break - # Even though try_path() didn't raise MissingError, it returned 0 - # files. src is probably a wildcard meant for some other pfx. Loop - # back to try the next. - - print "%d files" % count - - # Let caller check whether we processed as many files as expected. In - # particular, let caller notice 0. - return count + print "%d files" % len(found_files) + return found_files def do(self, *actions): self.actions = actions diff --git a/indra/lib/python/indra/util/llperformance.py b/indra/lib/python/indra/util/llperformance.py index 7c52730b5..57dd64de3 100755 --- a/indra/lib/python/indra/util/llperformance.py +++ b/indra/lib/python/indra/util/llperformance.py @@ -1,4 +1,28 @@ -#!/usr/bin/python +#!/usr/bin/env python +"""\ +@file llperformance.py + +$LicenseInfo:firstyear=2010&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2010-2011, Linden Research, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +$/LicenseInfo$ +""" # ------------------------------------------------ # Sim metrics utility functions. diff --git a/indra/lib/python/indra/util/llsubprocess.py b/indra/lib/python/indra/util/llsubprocess.py index c4c40739e..7e0e115d1 100644 --- a/indra/lib/python/indra/util/llsubprocess.py +++ b/indra/lib/python/indra/util/llsubprocess.py @@ -90,6 +90,17 @@ all the output, and get the result. child.tochild.close() result = child.poll() if result != -1: + # At this point, the child process has exited and result + # is the return value from the process. Between the time + # we called select() and poll() the process may have + # exited so read all the data left on the child process + # stdout and stderr. + last = child.fromchild.read() + if last: + out.append(last) + last = child.childerr.read() + if last: + err.append(last) child.tochild.close() child.fromchild.close() child.childerr.close() diff --git a/indra/lib/python/indra/util/named_query.py b/indra/lib/python/indra/util/named_query.py index 192e59275..6bf956107 100644 --- a/indra/lib/python/indra/util/named_query.py +++ b/indra/lib/python/indra/util/named_query.py @@ -40,7 +40,6 @@ from indra.base import llsd from indra.base import config DEBUG = False - NQ_FILE_SUFFIX = config.get('named-query-file-suffix', '.nq') NQ_FILE_SUFFIX_LEN = len(NQ_FILE_SUFFIX) @@ -52,6 +51,11 @@ def _init_g_named_manager(sql_dir = None): This function is intended entirely for testing purposes, because it's tricky to control the config from inside a test.""" + global NQ_FILE_SUFFIX + NQ_FILE_SUFFIX = config.get('named-query-file-suffix', '.nq') + global NQ_FILE_SUFFIX_LEN + NQ_FILE_SUFFIX_LEN = len(NQ_FILE_SUFFIX) + if sql_dir is None: sql_dir = config.get('named-query-base-dir') @@ -65,11 +69,11 @@ def _init_g_named_manager(sql_dir = None): _g_named_manager = NamedQueryManager( os.path.abspath(os.path.realpath(sql_dir))) -def get(name): +def get(name, schema = None): "Get the named query object to be used to perform queries" if _g_named_manager is None: _init_g_named_manager() - return _g_named_manager.get(name) + return _g_named_manager.get(name).for_schema(schema) def sql(connection, name, params): # use module-global NamedQuery object to perform default substitution @@ -280,7 +284,10 @@ class NamedQuery(object): So, we need a vendor (or extention) for LIKE_STRING. Anyone want to write it?""" - utf8_value = unicode(value, "utf-8") + if isinstance(value, unicode): + utf8_value = value + else: + utf8_value = unicode(value, "utf-8") esc_list = [] remove_chars = set(u"%_") for glyph in utf8_value: @@ -317,6 +324,8 @@ class NamedQuery(object): def for_schema(self, db_name): "Look trough the alternates and return the correct query" + if db_name is None: + return self try: return self._alternative[db_name] except KeyError, e: @@ -341,21 +350,21 @@ class NamedQuery(object): cursor = connection.cursor(MySQLdb.cursors.DictCursor) else: cursor = connection.cursor() - - statement = self.sql(connection, params) + + full_query, params = self._construct_sql(params) if DEBUG: - print "SQL:", statement - rows = cursor.execute(statement) - + print "SQL:", self.sql(connection, params) + rows = cursor.execute(full_query, params) + # *NOTE: the expect_rows argument is a very cheesy way to get some # validation on the result set. If you want to add more expectation - # logic, do something more object-oriented and flexible. Or use an ORM. + # logic, do something more object-oriented and flexible. Or use an ORM. if(self._return_as_map): expect_rows = 1 if expect_rows is not None and rows != expect_rows: cursor.close() - raise ExpectationFailed("Statement expected %s rows, got %s. Sql: %s" % ( - expect_rows, rows, statement)) + raise ExpectationFailed("Statement expected %s rows, got %s. Sql: '%s' %s" % ( + expect_rows, rows, full_query, params)) # convert to dicts manually if we're not using a dictcursor if use_dictcursor: @@ -381,11 +390,9 @@ class NamedQuery(object): return result_set[0] return result_set - def sql(self, connection, params): - """ Generates an SQL statement from the named query document - and a dictionary of parameters. - - """ + def _construct_sql(self, params): + """ Returns a query string and a dictionary of parameters, + suitable for directly passing to the execute() method.""" self.refresh() # build the query from the options available and the params @@ -431,10 +438,23 @@ class NamedQuery(object): new_params[self._build_integer_key(key)] = int(params[key]) params.update(new_params) + return full_query, params + + def sql(self, connection, params): + """ Generates an SQL statement from the named query document + and a dictionary of parameters. + + *NOTE: Only use for debugging, because it uses the + non-standard MySQLdb 'literal' method. + """ + if not DEBUG: + import warnings + warnings.warn("Don't use named_query.sql() when not debugging. Used on %s" % self._location) # do substitution using the mysql (non-standard) 'literal' # function to do the escaping. - sql = full_query % connection.literal(params) - return sql + full_query, params = self._construct_sql(params) + return full_query % connection.literal(params) + def refresh(self): """ Refresh self from the file on the filesystem. diff --git a/indra/lib/python/indra/util/simperf_oprof_interface.py b/indra/lib/python/indra/util/simperf_oprof_interface.py index c8d0f7475..547d2f998 100755 --- a/indra/lib/python/indra/util/simperf_oprof_interface.py +++ b/indra/lib/python/indra/util/simperf_oprof_interface.py @@ -3,20 +3,27 @@ @file simperf_oprof_interface.py @brief Manage OProfile data collection on a host -$LicenseInfo:firstyear=2008&license=internal$ +$LicenseInfo:firstyear=2008&license=mit$ Copyright (c) 2008-2009, Linden Research, Inc. -The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -this source code is governed by the Linden Lab Source Code Disclosure -Agreement ("Agreement") previously entered between you and Linden -Lab. By accessing, using, copying, modifying or distributing this -software, you acknowledge that you have been informed of your -obligations under the Agreement and agree to abide by those obligations. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -COMPLETENESS OR PERFORMANCE. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. $/LicenseInfo$ """ diff --git a/indra/lib/python/indra/util/simperf_proc_interface.py b/indra/lib/python/indra/util/simperf_proc_interface.py index 62a63fa87..de061f68c 100755 --- a/indra/lib/python/indra/util/simperf_proc_interface.py +++ b/indra/lib/python/indra/util/simperf_proc_interface.py @@ -1,4 +1,31 @@ -#!/usr/bin/python +#!/usr/bin/env python +"""\ +@file simperf_proc_interface.py +@brief Utility to extract log messages from *..llsd files containing performance statistics. + +$LicenseInfo:firstyear=2008&license=mit$ + +Copyright (c) 2008-2009, Linden Research, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +$/LicenseInfo$ +""" # ---------------------------------------------------- # Utility to extract log messages from *..llsd diff --git a/indra/lib/python/indra/util/test_win32_manifest.py b/indra/lib/python/indra/util/test_win32_manifest.py new file mode 100644 index 000000000..0532cb006 --- /dev/null +++ b/indra/lib/python/indra/util/test_win32_manifest.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +"""\ +@file test_win32_manifest.py +@brief Test an assembly binding version and uniqueness in a windows dll or exe. + +$LicenseInfo:firstyear=2009&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2009-2011, Linden Research, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +$/LicenseInfo$ +""" +import sys, os +import tempfile +from xml.dom.minidom import parse + +class AssemblyTestException(Exception): + pass + +class NoManifestException(AssemblyTestException): + pass + +class MultipleBindingsException(AssemblyTestException): + pass + +class UnexpectedVersionException(AssemblyTestException): + pass + +class NoMatchingAssemblyException(AssemblyTestException): + pass + +def get_HKLM_registry_value(key_str, value_str): + import _winreg + reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) + key = _winreg.OpenKey(reg, key_str) + value = _winreg.QueryValueEx(key, value_str)[0] + #print 'Found: %s' % value + return value + +def find_vc_dir(): + supported_versions = (r'8.0', r'9.0') + supported_products = (r'VisualStudio', r'VCExpress') + value_str = (r'ProductDir') + + for product in supported_products: + for version in supported_versions: + key_str = (r'SOFTWARE\Microsoft\%s\%s\Setup\VC' % + (product, version)) + try: + return get_HKLM_registry_value(key_str, value_str) + except WindowsError, err: + x64_key_str = (r'SOFTWARE\Wow6432Node\Microsoft\VisualStudio\%s\Setup\VS' % + version) + try: + return get_HKLM_registry_value(x64_key_str, value_str) + except: + print >> sys.stderr, "Didn't find MS %s version %s " % (product,version) + + raise + +def find_mt_path(): + vc_dir = find_vc_dir() + mt_path = '\"%sbin\\mt.exe\"' % vc_dir + return mt_path + +def test_assembly_binding(src_filename, assembly_name, assembly_ver): + print "checking %s dependency %s..." % (src_filename, assembly_name) + + (tmp_file_fd, tmp_file_name) = tempfile.mkstemp(suffix='.xml') + tmp_file = os.fdopen(tmp_file_fd) + tmp_file.close() + + mt_path = find_mt_path() + resource_id = "" + if os.path.splitext(src_filename)[1].lower() == ".dll": + resource_id = ";#2" + system_call = '%s -nologo -inputresource:%s%s -out:%s > NUL' % (mt_path, src_filename, resource_id, tmp_file_name) + print "Executing: %s" % system_call + mt_result = os.system(system_call) + if mt_result == 31: + print "No manifest found in %s" % src_filename + raise NoManifestException() + + manifest_dom = parse(tmp_file_name) + nodes = manifest_dom.getElementsByTagName('assemblyIdentity') + + versions = list() + for node in nodes: + if node.getAttribute('name') == assembly_name: + versions.append(node.getAttribute('version')) + + if len(versions) == 0: + print "No matching assemblies found in %s" % src_filename + raise NoMatchingAssemblyException() + + elif len(versions) > 1: + print "Multiple bindings to %s found:" % assembly_name + print versions + print + raise MultipleBindingsException(versions) + + elif versions[0] != assembly_ver: + print "Unexpected version found for %s:" % assembly_name + print "Wanted %s, found %s" % (assembly_ver, versions[0]) + print + raise UnexpectedVersionException(assembly_ver, versions[0]) + + os.remove(tmp_file_name) + + print "SUCCESS: %s OK!" % src_filename + print + +if __name__ == '__main__': + + print + print "Running test_win32_manifest.py..." + + usage = 'test_win32_manfest ' + + try: + src_filename = sys.argv[1] + assembly_name = sys.argv[2] + assembly_ver = sys.argv[3] + except: + print "Usage:" + print usage + print + raise + + test_assembly_binding(src_filename, assembly_name, assembly_ver) + + diff --git a/indra/lib/python/uuid.py b/indra/lib/python/uuid.py index 1c86c761b..0bc21a35f 100644 --- a/indra/lib/python/uuid.py +++ b/indra/lib/python/uuid.py @@ -45,13 +45,6 @@ Typical usage: This module works with Python 2.3 or higher.""" -# *HACK: Necessary for python 2.4. Consider replacing this code wart -# after python >=2.5 has deployed everywhere. 2009-10-05 -try: - from hashlib import md5 -except ImportError: - from md5 import md5 - __author__ = 'Ka-Ping Yee ' __date__ = '$Date: 2006/06/12 23:15:40 $'.split()[1].replace('/', '-') __version__ = '$Revision: 1.30 $'.split()[1] @@ -453,6 +446,13 @@ def uuid1(node=None, clock_seq=None): def uuid3(namespace, name): """Generate a UUID from the MD5 hash of a namespace UUID and a name.""" + try: + # Python 2.6 + from hashlib import md5 + except ImportError: + # Python 2.5 and earlier + from md5 import new as md5 + hash = md5(namespace.bytes + name).digest() return UUID(bytes=hash[:16], version=3) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index ce78f0fbd..af9604952 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1301,13 +1301,6 @@ if (WINDOWS) winspool ) - if(FMODSTUDIO) - list(APPEND viewer_LIBRARIES ${FMODSTUDIO_LIBRARY}) - endif(FMODSTUDIO) - - if(FMODEX) - list(APPEND viewer_LIBRARIES ${FMODEX_LIBRARY}) - endif(FMODEX) find_library(DEBUG_INTEL_MEMOPS_LIBRARY ll_intel_memops PATHS ${LIBS_PREBUILT_DIR}/lib/debug @@ -1411,20 +1404,16 @@ if (WINDOWS) list(APPEND viewer_SOURCE_FILES ${viewer_INSTALLER_FILES}) endif (WINDOWS) +if (FMODSTUDIO) + set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODSTUDIO") +endif (FMODSTUDIO) +if (FMODEX) + set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODEX") +endif (FMODEX) if (OPENAL) set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_OPENAL") endif (OPENAL) -if (FMODSTUDIO) - set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODSTUDIO") - set(FMODWRAPPER_LIBRARY ${FMODSTUDIO_LIBRARY}) -endif (FMODSTUDIO) - -if (FMODEX) - set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODEX") - set(FMODWRAPPER_LIBRARY ${FMODEX_LIBRARY}) -endif (FMODEX) - set_source_files_properties(llstartup.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}") list(APPEND viewer_SOURCE_FILES ${viewer_HEADER_FILES}) @@ -1445,44 +1434,45 @@ endif (!DISABLE_TEMPLATE_CHECK) set(PACKAGE OFF CACHE BOOL "Add a package_viewer target that builds an installer package.") +if(FMOD_LIBRARY_RELEASE) + get_filename_component(fmod_lib_rel_name ${FMOD_LIBRARY_RELEASE} NAME) + get_filename_component(fmod_lib_deb_name ${FMOD_LIBRARY_DEBUG} NAME) + add_custom_command(OUTPUT fmod_lib.marker + COMMENT "Copying fmod library to executable directory" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<$>:${FMOD_LIBRARY_RELEASE}>$<$:${FMOD_LIBRARY_DEBUG}>" "${CMAKE_CFG_INTDIR}/$<$>:${fmod_lib_rel_name}>$<$:${fmod_lib_deb_name}>" + DEPENDS "${FMOD_LIBRARY_RELEASE}") + add_custom_target(fmod_lib_copy DEPENDS fmod_lib.marker prepare) + if(DARWIN) + add_custom_command(OUTPUT fmod_lib_install.marker + COMMAND install_name_tool -id "@executable_path/../Resources/$<$>:${fmod_lib_rel_name}>$<$:${FMOD_LIBRARY_DEBUG}>" "${CMAKE_CFG_INTDIR}/$<$>:${fmod_lib_rel_name}>$<$:${fmod_lib_deb_name}>" + DEPENDS fmod_lib.marker) + add_dependencies(fmod_lib_copy fmod_lib_install.marker) + endif(DARWIN) + add_dependencies(${VIEWER_BINARY_NAME} fmod_lib_copy) + + #viewer_manifest.py needs these libraries (for now its all dynlib) + if(MANIFEST_LIBRARIES) + set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|optimized ${fmod_lib_rel_name}|debug ${fmod_lib_deb_name}") + else(MANIFEST_LIBRARIES) + set(MANIFEST_LIBRARIES "--extra_libraries=optimized ${fmod_lib_rel_name}|debug ${fmod_lib_deb_name}") + endif(MANIFEST_LIBRARIES) + + if(WINDOWS) #If windows, fmod_lib_ points to a dll. The correct .lib needs to be linked (but copying is not necessary) + set(EXTRA_LINKER_FLAGS_RELEASE "/DELAYLOAD:${fmod_lib_rel_name}") + set(EXTRA_LINKER_FLAGS_DEBUG "/DELAYLOAD:${fmod_lib_deb_name}") + endif(WINDOWS) + list(APPEND viewer_LIBRARIES optimized "${FMOD_LINK_LIBRARY_RELEASE}" debug "${FMOD_LINK_LIBRARY_DEBUG}" ) +endif(FMOD_LIBRARY_RELEASE) + if (WINDOWS) - set(release_flags "/MAPRelease/${VIEWER_BINARY_NAME}.map") - - if (FMODSTUDIO) - if (WORD_SIZE EQUAL 32) - set(fmodstudio_dll_file "fmod.dll") - else (WORD_SIZE EQUAL 32) - set(fmodstudio_dll_file "fmod64.dll") - endif (WORD_SIZE EQUAL 32) - - if(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${fmodstudio_dll_file}") - else(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "--extra_libraries=${fmodstudio_dll_file}") - endif(MANIFEST_LIBRARIES) - set(EXTRA_LINKER_FLAGS "/DELAYLOAD:${fmodstudio_dll_file}") - endif (FMODSTUDIO) - - if (FMODEX) - if (WORD_SIZE EQUAL 32) - set(fmodex_dll_file "fmodex.dll") - else (WORD_SIZE EQUAL 32) - set(fmodex_dll_file "fmodex64.dll") - endif (WORD_SIZE EQUAL 32) - - if(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${fmodex_dll_file}") - else(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "--extra_libraries=${fmodex_dll_file}") - endif(MANIFEST_LIBRARIES) - set(EXTRA_LINKER_FLAGS "/DELAYLOAD:${fmodex_dll_file}") - endif (FMODEX) + set(release_flags "/MAP") set_target_properties(${VIEWER_BINARY_NAME} PROPERTIES - LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS ${GOOGLE_PERFTOOLS_LINKER_FLAGS} ${EXTRA_LINKER_FLAGS}" - LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\"" - LINK_FLAGS_RELEASE ${release_flags} + LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS ${GOOGLE_PERFTOOLS_LINKER_FLAGS}" + LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\" ${EXTRA_LINKER_FLAGS_DEBUG}" + LINK_FLAGS_RELEASE "${release_flags} ${EXTRA_LINKER_FLAGS_RELEASE}" + LINK_FLAGS_RELWITHDEBINFO "${release_flags} ${EXTRA_LINKER_FLAGS_RELEASE}" ) # sets the 'working directory' for debugging from visual studio. @@ -1621,7 +1611,6 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${Boost_DATE_TIME_LIBRARY} ${DBUSGLIB_LIBRARIES} ${OPENGL_LIBRARIES} - ${FMODWRAPPER_LIBRARY} # must come after LLAudio ${GLOD_LIBRARIES} ${APRUTIL_LIBRARIES} ${OPENGL_LIBRARIES} @@ -1647,22 +1636,6 @@ if (LINUX) set(product ${VIEWER_BRANDING_NAME_CAMELCASE}-${ARCH}-${viewer_VERSION}) - if (FMODSTUDIO) - if(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${FMODSTUDIO_LIBRARY}") - else(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "--extra_libraries=${FMODSTUDIO_LIBRARY}") - endif(MANIFEST_LIBRARIES) - endif (FMODSTUDIO) - - if (FMODEX) - if(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${FMODEX_LIBRARY}") - else(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "--extra_libraries=${FMODEX_LIBRARY}") - endif(MANIFEST_LIBRARIES) - endif (FMODEX) - add_custom_command( OUTPUT ${product}.tar.bz2 COMMAND ${PYTHON_EXECUTABLE} @@ -1755,24 +1728,6 @@ if (DARWIN) add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_webkit basic_plugin_filepicker) - if (FMODSTUDIO) - add_custom_command(OUTPUT "${FMODSTUDIO_LIBRARY}" - COMMAND cp "${FMODSTUDIO_ORIG_LIBRARY}" "${FMODSTUDIO_LIBRARY}" - COMMAND install_name_tool -id "@executable_path/../Resources/libfmod.dylib" ${FMODSTUDIO_LIBRARY} - DEPENDS "${FMODSTUDIO_ORIG_LIBRARY}") - add_custom_target(fmodstudio_modified_library DEPENDS "${FMODSTUDIO_LIBRARY}") - add_dependencies(${VIEWER_BINARY_NAME} fmodstudio_modified_library) - endif (FMODSTUDIO) - - if (FMODEX) - add_custom_command(OUTPUT "${FMODEX_LIBRARY}" - COMMAND cp "${FMODEX_ORIG_LIBRARY}" "${FMODEX_LIBRARY}" - COMMAND install_name_tool -id "@executable_path/../Resources/libfmodex.dylib" ${FMODEX_LIBRARY} - DEPENDS "${FMODEX_ORIG_LIBRARY}") - add_custom_target(fmodex_modified_library DEPENDS "${FMODEX_LIBRARY}") - add_dependencies(${VIEWER_BINARY_NAME} fmodex_modified_library) - endif (FMODEX) - if (PACKAGE) add_custom_target(package_viewer ALL DEPENDS ${VIEWER_BINARY_NAME}) @@ -1817,9 +1772,9 @@ if (PACKAGE) list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}") # *TODO: Generate these search dirs in the cmake files related to each binary. list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/llplugin/slplugin/${CMAKE_CFG_INTDIR}") - list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/media_plugins/gstreamer010/${CMAKE_CFG_INTDIR}") - list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/media_plugins/quicktime/${CMAKE_CFG_INTDIR}") - list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/media_plugins/webkit/${CMAKE_CFG_INTDIR}") + list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/plugins/gstreamer010/${CMAKE_CFG_INTDIR}") + list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/plugins/quicktime/${CMAKE_CFG_INTDIR}") + list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/plugins/webkit/${CMAKE_CFG_INTDIR}") set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-darwin.tar.bz2") set(VIEWER_EXE_GLOBS "'${VIEWER_BRANDING_NAME}' SLPlugin") set(VIEWER_LIB_GLOB "*.dylib") @@ -1897,8 +1852,10 @@ if (WINDOWS) set_target_properties(llcommon PROPERTIES - LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /LTCG" + LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT" LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\"" + LINK_FLAGS_RELEASE "${release_flags} /LTCG" + LINK_FLAGS_RELWITHDEBINFO "${release_flags}" ) add_custom_command( diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 5828e8c90..6e9466449 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -176,6 +176,42 @@ class ViewerManifest(LLManifest): def icon_path(self): return "../../indra/newview/res/" + + def path_optional(self, src, dst=None): + """ + For a number of our self.path() calls, not only do we want + to deal with the absence of src, we also want to remember + which were present. Return either an empty list (absent) + or a list containing dst (present). Concatenate these + return values to get a list of all libs that are present. + """ + found_files = [] + try: + found_files = self.path(src, dst) + except RuntimeError, err: + pass + if not found_files: + print "Skipping %s" % dst + return found_files + + def add_extra_libraries(self): + found_libs = [] + if 'extra_libraries' in self.args: + path_list = self.args['extra_libraries'].split('|') + for cur_path in path_list: + config, file = cur_path.split(' ', 1) + if(config == 'optimized'): + if(self.args['configuration'].lower() != 'release' and self.args['configuration'].lower() != 'relwithdebinfo'): + continue + cur_path = file + if(config == 'debug'): + if(self.args['configuration'].lower() != 'debug'): + continue + cur_path = file + if(cur_path != None): + found_libs += self.path_optional(cur_path) + return found_libs + class WindowsManifest(ViewerManifest): def is_win64(self): return self.args.get('arch') == "x86_64" @@ -189,56 +225,75 @@ class WindowsManifest(ViewerManifest): # the final exe is complicated because we're not sure where it's coming from, # nor do we have a fixed name for the executable self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe()) - - release_lib_dir = "Release" # Plugin host application self.path2basename(os.path.join(os.pardir, 'llplugin', 'slplugin', self.args['configuration']), "SLplugin.exe") - # Plugin volume control - if not self.is_win64() and self.prefix(src=self.args['configuration'], dst=""): - self.path("winmm.dll") + + # Get llcommon and deps. If missing assume static linkage and continue. + if self.prefix(src=self.args['configuration'], dst=""): + try: + self.path('llcommon.dll') + except RuntimeError, err: + print err.message + print "Skipping llcommon.dll (assuming llcommon was linked statically)" + + try: + self.path('libapr-1.dll') + self.path('libaprutil-1.dll') + self.path('libapriconv-1.dll') + except RuntimeError, err: + pass + + # For mesh upload + if not self.is_win64(): + self.path("libcollada14dom22.dll") + + self.path("glod.dll") + + self.add_extra_libraries() + + try: + self.path("msvc*.dll") + except: + try: + if self.prefix(src="msvcrt", dst=""): + self.path("*.dll") + self.path("*.manifest") + self.end_prefix() + except: + pass + + # Vivox runtimes + self.path("SLVoice.exe") + self.path("vivoxsdk.dll") + self.path("ortp.dll") + self.path("libsndfile-1.dll") + self.path("zlib1.dll") + self.path("vivoxplatform.dll") + self.path("vivoxoal.dll") + self.path("ca-bundle.crt") + + # Security + self.path("ssleay32.dll") + self.path("libeay32.dll") + + # For spellchecking + self.path("libhunspell.dll") + + # For google-perftools tcmalloc allocator. + if not self.is_win64(): + try: + self.path('libtcmalloc_minimal.dll') + except: + print "Skipping libtcmalloc_minimal.dll" self.end_prefix() self.path(src="licenses-win32.txt", dst="licenses.txt") - self.path("featuretable.txt") - # For spellchecking - if self.prefix(src=self.args['configuration'], dst=""): - self.path("libhunspell.dll") - self.end_prefix() - - # For mesh upload - if not self.is_win64() and self.prefix(src=self.args['configuration'], dst=""): - self.path("libcollada14dom22.dll") - self.end_prefix() - - if self.prefix(src=self.args['configuration'], dst=""): - self.path("glod.dll") - self.end_prefix() - - # For use in crash reporting (generates minidumps) - #self.path("dbghelp.dll") - #is shipped with windows anyway - - # For using FMOD for sound... DJS - #~if self.prefix(src=release_lib_dir, dst=""): - #~try: - #~self.path("fmod.dll") - #~pass - #~except: - #~print "Skipping fmod.dll - not found" - #~ pass - #~self.end_prefix() - - # For textures - #if self.prefix(src=release_lib_dir, dst=""): - # self.path("openjpeg.dll") - # self.end_prefix() - # Plugins - FilePicker if self.prefix(src='../plugins/filepicker/%s' % self.args['configuration'], dst="llplugin"): self.path("basic_plugin_filepicker.dll") @@ -254,8 +309,13 @@ class WindowsManifest(ViewerManifest): self.path("media_plugin_webkit.dll") self.end_prefix() + # Plugin volume control + if not self.is_win64() and self.prefix(src='../plugins/winmmshim/%s' % self.args['configuration'], dst=""): + self.path("winmm.dll") + self.end_prefix() + # For WebKit/Qt plugin runtimes - if self.prefix(src=release_lib_dir+"/llplugin", dst="llplugin"): + if self.prefix(src=self.args['configuration']+"/llplugin", dst="llplugin"): self.path("libeay32.dll") self.path("qtcore4.dll") self.path("qtgui4.dll") @@ -264,82 +324,29 @@ class WindowsManifest(ViewerManifest): self.path("qtwebkit4.dll") self.path("qtxmlpatterns4.dll") self.path("ssleay32.dll") + + # For WebKit/Qt plugin runtimes (image format plugins) + if self.prefix(src="imageformats", dst="imageformats"): + self.path("qgif4.dll") + self.path("qico4.dll") + self.path("qjpeg4.dll") + self.path("qmng4.dll") + self.path("qsvg4.dll") + self.path("qtiff4.dll") + self.end_prefix() + + if self.prefix(src="codecs", dst="codecs"): + self.path("qcncodecs4.dll") + self.path("qjpcodecs4.dll") + self.path("qkrcodecs4.dll") + self.path("qtwcodecs4.dll") + self.end_prefix() + self.end_prefix() - # For WebKit/Qt plugin runtimes (image format plugins) - if self.prefix(src=release_lib_dir+"/llplugin/imageformats", dst="llplugin/imageformats"): - self.path("qgif4.dll") - self.path("qico4.dll") - self.path("qjpeg4.dll") - self.path("qmng4.dll") - self.path("qsvg4.dll") - self.path("qtiff4.dll") - self.end_prefix() - - if self.prefix(src=release_lib_dir+"/llplugin/codecs", dst="llplugin/codecs"): - self.path("qcncodecs4.dll") - self.path("qjpcodecs4.dll") - self.path("qkrcodecs4.dll") - self.path("qtwcodecs4.dll") - self.end_prefix() - - # Get llcommon and deps. If missing assume static linkage and continue. - if self.prefix(src=self.args['configuration'], dst=""): - try: - self.path('llcommon.dll') - except RuntimeError, err: - print err.message - print "Skipping llcommon.dll (assuming llcommon was linked statically)" - self.end_prefix() - if self.prefix(src=release_lib_dir, dst=""): - self.path("libeay32.dll") - self.path("ssleay32.dll") - try: - self.path('libapr-1.dll') - self.path('libaprutil-1.dll') - self.path('libapriconv-1.dll') - except RuntimeError, err: - pass - self.end_prefix() - - # For google-perftools tcmalloc allocator. - if not self.is_win64(): - self.path(release_lib_dir+"/libtcmalloc_minimal.dll", dst="libtcmalloc_minimal.dll") - - try: - self.path("msvc*.dll") - except: - try: - if self.prefix(src=release_lib_dir+"/msvcrt", dst=""): - self.path("*.dll") - self.path("*.manifest") - self.end_prefix() - except: - pass - - # Vivox runtimes - if self.prefix(src=release_lib_dir, dst=""): - self.path("SLVoice.exe") - self.path("ca-bundle.crt") - self.path("libsndfile-1.dll") - self.path("ortp.dll") - self.path("vivoxoal.dll") - self.path("vivoxplatform.dll") - self.path("vivoxsdk.dll") - self.path("zlib1.dll") - self.end_prefix() - - if 'extra_libraries' in self.args: - print self.args['extra_libraries'] - path_list = self.args['extra_libraries'].split('|') - for path in path_list: - if self.prefix(src=release_lib_dir, dst=""): - self.path(path) - self.end_prefix() self.package_file = 'none' - def nsi_file_commands(self, install=True): def wpath(path): if path.endswith('/') or path.endswith(os.path.sep): @@ -357,7 +364,7 @@ class WindowsManifest(ViewerManifest): for pkg_file in dest_files: rel_file = os.path.normpath(pkg_file.replace(self.get_dst_prefix()+os.path.sep,'')) installed_dir = wpath(os.path.join('$INSTDIR', os.path.dirname(rel_file))) - pkg_file = wpath(os.path.normpath(pkg_file)) + pkg_file = wpath(os.path.join(os.pardir,os.path.normpath(pkg_file))) if installed_dir != out_path: if install: out_path = installed_dir @@ -392,22 +399,6 @@ class WindowsManifest(ViewerManifest): mask = "%s_%s_Setup.exe" return mask % (self.channel_oneword(), '-'.join(self.args['version'])) - - def sign_command(self, *argv): - return [ - "signtool.exe", - "sign", "/v", - "/f",os.environ['VIEWER_SIGNING_KEY'], - "/p",os.environ['VIEWER_SIGNING_PASSWORD'], - "/d","%s" % self.channel(), - "/du",os.environ['VIEWER_SIGNING_URL'], - "/t","http://timestamp.comodoca.com/authenticode" - ] + list(argv) - - - def sign(self, *argv): - subprocess.check_call(self.sign_command(*argv)) - def package_finish(self): # a standard map of strings for replacing in the templates substitution_strings = { @@ -454,20 +445,12 @@ class WindowsManifest(ViewerManifest): installer_file = installer_file % substitution_strings substitution_strings['installer_file'] = installer_file - # Sign the binaries - if 'VIEWER_SIGNING_PASSWORD' in os.environ: - try: - self.sign(self.args['configuration']+"\\"+self.final_exe()) - self.sign(self.args['configuration']+"\\SLPlugin.exe") - self.sign(self.args['configuration']+"\\SLVoice.exe") - except Exception, e: - print "Couldn't sign binaries. Tried to sign %s" % self.args['configuration'] + "\\" + self.final_exe() + "\nException: %s" % e tempfile = "secondlife_setup_tmp.nsi" # the following replaces strings in the nsi template # it also does python-style % substitution self.replace_in("installers/windows/installer_template.nsi", tempfile, { "%%VERSION%%":version_vars, - "%%SOURCE%%":self.get_src_prefix(), + "%%SOURCE%%":os.path.abspath(self.get_src_prefix()), "%%GRID_VARS%%":grid_vars_template % substitution_strings, "%%INSTALL_FILES%%":self.nsi_file_commands(True), "%%DELETE_FILES%%":self.nsi_file_commands(False), @@ -489,14 +472,14 @@ class WindowsManifest(ViewerManifest): NSIS_path = os.environ['ProgramFiles(X86)'] + '\\NSIS\\Unicode\\makensis.exe' self.run_command([proper_windows_path(NSIS_path),self.dst_path_of(tempfile)]) # self.remove(self.dst_path_of(tempfile)) - - # Sign the installer - if 'VIEWER_SIGNING_PASSWORD' in os.environ: - try: - self.sign(self.args['configuration'] + "\\" + substitution_strings['installer_file']) - except Exception, e: - print "Couldn't sign windows installer. Tried to sign %s" % self.args['configuration'] + "\\" + substitution_strings['installer_file'] + "\nException: %s" % e - + # If we're on a build machine, sign the code using our Authenticode certificate. JC + sign_py = os.path.expandvars("{SIGN_PY}") + if sign_py == "" or sign_py == "{SIGN_PY}": + sign_py = 'C:\\buildscripts\\code-signing\\sign.py' + if os.path.exists(sign_py): + self.run_command('python ' + sign_py + ' ' + self.dst_path_of(installer_file)) + else: + print "Skipping code signing,", sign_py, "does not exist" self.created_path(self.dst_path_of(installer_file)) self.package_file = installer_file @@ -508,10 +491,6 @@ class DarwinManifest(ViewerManifest): if self.prefix(src="", dst="Contents"): # everything goes in Contents - # copy additional libs in /Contents/MacOS/ - self.path("../packages/libraries/universal-darwin/lib/release/libndofdev.dylib", dst="Resources/libndofdev.dylib") - self.path("../packages/libraries/universal-darwin/lib/release/libhunspell-1.3.0.dylib", dst="Resources/libhunspell-1.3.0.dylib") - # most everything goes in the Resources directory if self.prefix(src="", dst="Resources"): super(DarwinManifest, self).construct() @@ -547,20 +526,8 @@ class DarwinManifest(ViewerManifest): self.path("uk.lproj") self.path("zh-Hans.lproj") - def path_optional(src, dst): - """ - For a number of our self.path() calls, not only do we want - to deal with the absence of src, we also want to remember - which were present. Return either an empty list (absent) - or a list containing dst (present). Concatenate these - return values to get a list of all libs that are present. - """ - if self.path(src, dst): - return [dst] - print "Skipping %s" % dst - return [] - - libdir = "../packages/libraries/universal-darwin/lib/release" + libdir = "../packages/lib/release" + alt_libdir = "../packages/libraries/universal-darwin/lib/release" # dylibs is a list of all the .dylib files we expect to need # in our bundled sub-apps. For each of these we'll create a # symlink from sub-app/Contents/Resources to the real .dylib. @@ -568,57 +535,40 @@ class DarwinManifest(ViewerManifest): libfile = "libllcommon.dylib" - dylibs = path_optional(self.find_existing_file(os.path.join(os.pardir, + dylibs = self.path_optional(self.find_existing_file(os.path.join(os.pardir, "llcommon", self.args['configuration'], libfile), os.path.join(libdir, libfile)), dst=libfile) - for libfile in ( - "libapr-1.0.dylib", - "libaprutil-1.0.dylib", - "libcollada14dom.dylib", - "libexpat.1.5.2.dylib", - "libexception_handler.dylib", - "libGLOD.dylib", - ): - dylibs += path_optional(os.path.join(libdir, libfile), libfile) + if self.prefix(src=libdir, alt_build=alt_libdir, dst=""): + for libfile in ( + "libapr-1.0.dylib", + "libaprutil-1.0.dylib", + "libcollada14dom.dylib", + "libexpat.1.5.2.dylib", + "libexception_handler.dylib", + "libGLOD.dylib", + "libhunspell-1.3.0.dylib", + "libndofdev.dylib", + ): + dylibs += self.path_optional(libfile) - # SLVoice and vivox lols, no symlinks needed - for libfile in ( + for libfile in ( 'libortp.dylib', 'libsndfile.dylib', 'libvivoxoal.dylib', 'libvivoxsdk.dylib', 'libvivoxplatform.dylib', 'ca-bundle.crt', - 'SLVoice', + 'SLVoice' ): - self.path2basename(libdir, libfile) + self.path(libfile) + + dylibs += self.add_extra_libraries() - # For using FMOD for sound...but, fmod is proprietary so some might not use it... - try: - self.path(self.args['configuration'] + "/libfmodwrapper.dylib", "libfmodwrapper.dylib") - pass - except: - print "Skipping libfmodwrapper.dylib - not found" - pass - - - # dylibs that vary based on configuration - if self.args['configuration'].lower() == 'debug': - for libfile in ( - "libfmodexL.dylib", - ): - dylibs += path_optional(os.path.join("../packages/lib/debug", - libfile), libfile) - else: - for libfile in ( - "libfmodex.dylib", - ): - dylibs += path_optional(os.path.join("../packages/lib/release", - libfile), libfile) + self.end_prefix() # our apps for app_bld_dir, app in (#("mac_crash_logger", "mac-crash-logger.app"), @@ -634,8 +584,8 @@ class DarwinManifest(ViewerManifest): # create a symlink to the real copy of the dylib. resource_path = self.dst_path_of(os.path.join(app, "Contents", "Resources")) for libfile in dylibs: - symlinkf(os.path.join(os.pardir, os.pardir, os.pardir, libfile), - os.path.join(resource_path, libfile)) + symlinkf(os.path.join(os.pardir, os.pardir, os.pardir, os.path.basename(libfile)), + os.path.join(resource_path, os.path.basename(libfile))) # plugins if self.prefix(src="", dst="llplugin"): @@ -675,37 +625,6 @@ class DarwinManifest(ViewerManifest): if not self.default_channel_for_brand(): channel_standin = self.channel() - # Sign the app if we have a key. - try: - signing_password = os.environ['VIEWER_SIGNING_PASSWORD'] - except KeyError: - print "Skipping code signing" - pass - else: - home_path = os.environ['HOME'] - - self.run_command('security unlock-keychain -p "%s" "%s/Library/Keychains/viewer.keychain"' % (signing_password, home_path)) - signed=False - sign_attempts=3 - sign_retry_wait=15 - while (not signed) and (sign_attempts > 0): - try: - sign_attempts-=1; - self.run_command('codesign --verbose --force --timestamp --keychain "%(home_path)s/Library/Keychains/viewer.keychain" -s %(identity)r -f %(bundle)r' % { - 'home_path' : home_path, - 'identity': os.environ['VIEWER_SIGNING_KEY'], - 'bundle': self.get_dst_prefix() - }) - signed=True - except: - if sign_attempts: - print >> sys.stderr, "codesign failed, waiting %d seconds before retrying" % sign_retry_wait - time.sleep(sign_retry_wait) - sign_retry_wait*=2 - else: - print >> sys.stderr, "Maximum codesign attempts exceeded; giving up" - raise - imagename=self.installer_prefix() + '_'.join(self.args['version']) # See Ambroff's Hack comment further down if you want to create new bundles and dmg @@ -729,88 +648,64 @@ class DarwinManifest(ViewerManifest): 'vol':volname}) # mount the image and get the name of the mount point and device node - hdi_output = self.run_command('hdiutil attach -private %r' % sparsename) - try: - devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip() - volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip() + hdi_output = self.run_command('hdiutil attach -private "' + sparsename + '"') + devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip() + volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip() - if devfile != '/dev/disk1': - # adding more debugging info based upon nat's hunches to the - # logs to help track down 'SetFile -a V' failures -brad - print "WARNING: 'SetFile -a V' command below is probably gonna fail" + # Copy everything in to the mounted .dmg - # Copy everything in to the mounted .dmg + if self.default_channel_for_brand() and not self.default_grid(): + app_name = self.app_name() + " " + self.args['grid'] + else: + app_name = channel_standin.strip() - if self.default_channel_for_brand() and not self.default_grid(): - app_name = self.app_name() + " " + self.args['grid'] - else: - app_name = channel_standin.strip() + # Hack: + # Because there is no easy way to coerce the Finder into positioning + # the app bundle in the same place with different app names, we are + # adding multiple .DS_Store files to svn. There is one for release, + # one for release candidate and one for first look. Any other channels + # will use the release .DS_Store, and will look broken. + # - Ambroff 2008-08-20 + # Added a .DS_Store for snowglobe - Merov 2009-06-17 - # Hack: - # Because there is no easy way to coerce the Finder into positioning - # the app bundle in the same place with different app names, we are - # adding multiple .DS_Store files to svn. There is one for release, - # one for release candidate and one for first look. Any other channels - # will use the release .DS_Store, and will look broken. - # - Ambroff 2008-08-20 - # Added a .DS_Store for snowglobe - Merov 2009-06-17 + # We have a single branded installer for all snowglobe channels so snowglobe logic is a bit different + if (self.app_name()=="Snowglobe"): + dmg_template = os.path.join ('installers', 'darwin', 'snowglobe-dmg') + else: + dmg_template = os.path.join( + 'installers', + 'darwin', + '%s-dmg' % "".join(self.channel_unique().split()).lower()) - # We have a single branded installer for all snowglobe channels so snowglobe logic is a bit different - if (self.app_name()=="Snowglobe"): - dmg_template = os.path.join ('installers', 'darwin', 'snowglobe-dmg') - else: - dmg_template = os.path.join( - 'installers', - 'darwin', - '%s-dmg' % "".join(self.channel_unique().split()).lower()) + if not os.path.exists (self.src_path_of(dmg_template)): + dmg_template = os.path.join ('installers', 'darwin', 'release-dmg') - if not os.path.exists (self.src_path_of(dmg_template)): - dmg_template = os.path.join ('installers', 'darwin', 'release-dmg') + for s,d in {self.get_dst_prefix():app_name + ".app", + os.path.join(dmg_template, "_VolumeIcon.icns"): ".VolumeIcon.icns", + os.path.join(dmg_template, "background.jpg"): "background.jpg", + os.path.join(dmg_template, "_DS_Store"): ".DS_Store"}.items(): + print "Copying to dmg", s, d + self.copy_action(self.src_path_of(s), os.path.join(volpath, d)) - for s,d in {self.get_dst_prefix():app_name + ".app", - os.path.join(dmg_template, "_VolumeIcon.icns"): ".VolumeIcon.icns", - os.path.join(dmg_template, "background.jpg"): "background.jpg", - os.path.join(dmg_template, "_DS_Store"): ".DS_Store"}.items(): - print "Copying to dmg", s, d - self.copy_action(self.src_path_of(s), os.path.join(volpath, d)) + # Hide the background image, DS_Store file, and volume icon file (set their "visible" bit) + self.run_command('SetFile -a V "' + os.path.join(volpath, ".VolumeIcon.icns") + '"') + self.run_command('SetFile -a V "' + os.path.join(volpath, "background.jpg") + '"') + self.run_command('SetFile -a V "' + os.path.join(volpath, ".DS_Store") + '"') - # Hide the background image, DS_Store file, and volume icon file (set their "visible" bit) - for f in ".VolumeIcon.icns", "background.jpg", ".DS_Store": - pathname = os.path.join(volpath, f) - # We've observed mysterious "no such file" failures of the SetFile - # command, especially on the first file listed above -- yet - # subsequent inspection of the target directory confirms it's - # there. Timing problem with copy command? Try to handle. - for x in xrange(3): - if os.path.exists(pathname): - print "Confirmed existence: %r" % pathname - break - print "Waiting for %s copy command to complete (%s)..." % (f, x+1) - sys.stdout.flush() - time.sleep(1) - # If we fall out of the loop above without a successful break, oh - # well, possibly we've mistaken the nature of the problem. In any - # case, don't hang up the whole build looping indefinitely, let - # the original problem manifest by executing the desired command. - self.run_command('SetFile -a V %r' % pathname) + # Create the alias file (which is a resource file) from the .r + self.run_command('rez "' + self.src_path_of("installers/darwin/release-dmg/Applications-alias.r") + '" -o "' + os.path.join(volpath, "Applications") + '"') - # Create the alias file (which is a resource file) from the .r - self.run_command('Rez %r -o %r' % - (self.src_path_of("installers/darwin/release-dmg/Applications-alias.r"), - os.path.join(volpath, "Applications"))) + # Set the alias file's alias and custom icon bits + self.run_command('SetFile -a AC "' + os.path.join(volpath, "Applications") + '"') - # Set the alias file's alias and custom icon bits - self.run_command('SetFile -a AC "' + os.path.join(volpath, "Applications") + '"') + # Set the disk image root's custom icon bit + self.run_command('SetFile -a C "' + volpath + '"') - # Set the disk image root's custom icon bit - self.run_command('SetFile -a C "' + volpath + '"') - finally: - # Unmount the image - self.run_command('hdiutil detach -force "' + devfile + '"') + # Unmount the image + self.run_command('hdiutil detach -force "' + devfile + '"') print "Converting temp disk image to final disk image" self.run_command('hdiutil convert "%(sparse)s" -format UDZO -imagekey zlib-level=9 -o "%(final)s"' % {'sparse':sparsename, 'final':finalname}) - self.run_command('hdiutil internet-enable -yes "%(final)s"' % {'final':finalname}) # get rid of the temp file self.package_file = finalname self.remove(sparsename) @@ -848,7 +743,6 @@ class LinuxManifest(ViewerManifest): #else: # self.path("secondlife-bin","bin/"+self.binary_name()) if self.prefix(src="", dst="bin"): - self.path("secondlife-bin",self.binary_name()) self.path2basename("../llplugin/slplugin", "SLPlugin") self.end_prefix("bin") @@ -864,6 +758,10 @@ class LinuxManifest(ViewerManifest): self.path("../plugins/gstreamer010", "libmedia_plugin_gstreamer.so") self.end_prefix("bin/llplugin") + # llcommon + if not self.path("../llcommon/libllcommon.so", "lib/libllcommon.so"): + print "Skipping llcommon.so (assuming llcommon was linked statically)" + self.path("featuretable_linux.txt") def wrapper_name(self): @@ -930,14 +828,10 @@ class Linux_i686Manifest(LinuxManifest): def construct(self): super(Linux_i686Manifest, self).construct() - if not self.path("../llcommon/libllcommon.so", "lib/libllcommon.so"): - print "Skipping llcommon.so (assuming llcommon was linked statically)" - - if (not self.standalone()) and self.prefix("../packages/lib/release", dst="lib"): - self.prefix("../packages/libraries/i686-linux/lib/release", dst="lib") - + if (not self.standalone()) and self.prefix(src="../packages/lib/release", alt_build="../packages/libraries/i686-linux/lib/release", dst="lib"): self.path("libapr-1.so*") self.path("libaprutil-1.so*") + self.path("libdb*.so") self.path("libexpat.so*") self.path("libglod.so") @@ -946,20 +840,19 @@ class Linux_i686Manifest(LinuxManifest): self.path("libdirectfb-1.*.so*") self.path("libfusion-1.*.so*") self.path("libdirect-1.*.so*") - self.path("libhunspell-*.so.*") + self.path("libminizip.so.1.2.3", "libminizip.so"); + self.path("libhunspell-*.so.*") + # OpenAL self.path("libalut.so") self.path("libopenal.so.1") - self.path("libtcmalloc_minimal.so.0") #formerly called google perf tools + self.path("libcollada14dom.so.2.2", "libcollada14dom.so") + self.path("libcrypto.so*") + self.path("libELFIO.so") + self.path("libssl.so*") + self.path("libtcmalloc_minimal.so.0") self.path("libtcmalloc_minimal.so.0.2.2") - - try: - self.path("libfmod-3.75.so") - pass - except: - print "Skipping libfmod-3.75.so - not found" - pass # Boost self.path("libboost_context-mt.so.*") @@ -969,23 +862,11 @@ class Linux_i686Manifest(LinuxManifest): self.path("libboost_signals-mt.so.*") self.path("libboost_system-mt.so.*") self.path("libboost_thread-mt.so.*") - - self.path("libcollada14dom.so.2.2", "libcollada14dom.so") - self.path("libcrypto.so*") - self.path("libELFIO.so") - self.path("libminizip.so.1.2.3", "libminizip.so"); - self.path("libssl.so*") - if 'extra_libraries' in self.args: - path_list = self.args['extra_libraries'].split('|') - for path in path_list: - src_path = os.path.realpath(path) - dst_path = os.path.basename(path) - self.path(src_path, dst_path) + self.add_extra_libraries() self.end_prefix("lib") - self.end_prefix("lib") - + # Vivox runtimes if self.prefix(src="../packages/lib/release", dst="bin"): self.path("SLVoice") @@ -995,28 +876,33 @@ class Linux_i686Manifest(LinuxManifest): self.path("libvivoxsdk.so") self.end_prefix("lib") - class Linux_x86_64Manifest(LinuxManifest): def construct(self): super(Linux_x86_64Manifest, self).construct() - if not self.path("../llcommon/libllcommon.so", "lib64/libllcommon.so"): - print "Skipping llcommon.so (assuming llcommon was linked statically)" - - if (not self.standalone()) and self.prefix("../packages/lib/release", dst="lib64"): - self.prefix("../../libraries/x86_64-linux/lib/release", dst="lib64") + if (not self.standalone()) and self.prefix(src="../packages/lib/release", alt_build="../packages/libraries/x86_64-linux/lib/release", dst="lib"): self.path("libapr-1.so*") self.path("libaprutil-1.so*") + self.path("libdb-*.so*") + self.path("libexpat.so*") self.path("libglod.so") + self.path("libssl.so*") self.path("libuuid.so*") self.path("libSDL-1.2.so*") - self.path("libjpeg.so*") + self.path("libminizip.so.1.2.3", "libminizip.so"); self.path("libhunspell-1.3.so*") + # OpenAL self.path("libalut.so*") self.path("libopenal.so*") + self.path("libcollada14dom.so.2.2", "libcollada14dom.so") + self.path("libcrypto.so.*") + self.path("libELFIO.so") + self.path("libjpeg.so*") + self.path("libpng*.so*") + self.path("libz.so*") # Boost self.path("libboost_context-mt.so.*") @@ -1027,43 +913,30 @@ class Linux_x86_64Manifest(LinuxManifest): self.path("libboost_system-mt.so.*") self.path("libboost_thread-mt.so.*") - self.path("libcollada14dom.so.2.2", "libcollada14dom.so") - self.path("libcrypto.so.*") - self.path("libELFIO.so") - self.path("libminizip.so.1.2.3", "libminizip.so"); - self.path("libpng*.so*") - self.path("libssl.so*") - self.path("libz.so*") + self.add_extra_libraries() - if 'extra_libraries' in self.args: - path_list = self.args['extra_libraries'].split('|') - for path in path_list: - src_path = os.path.realpath(path) - dst_path = os.path.basename(path) - self.path(src_path, dst_path) + self.end_prefix("lib") - self.end_prefix("lib64") - self.end_prefix("lib64") + # Vivox runtimes + if self.prefix(src="../packages/lib/release", dst="bin"): + self.path("SLVoice") + self.end_prefix("bin") - # Vivox runtimes and libs - if self.prefix(src="../packages/lib/release", dst="bin"): - self.path("SLVoice") - self.end_prefix("bin") + if self.prefix(src="../packages/lib/release", dst="lib32"): + #self.path("libalut.so") + self.path("libortp.so") + self.path("libvivoxsdk.so") + self.end_prefix("lib32") - if self.prefix(src="../packages/lib/release", dst="lib32"): - #self.path("libalut.so") - self.path("libortp.so") - self.path("libvivoxsdk.so") - self.end_prefix("lib32") - - # 32bit libs needed for voice - if self.prefix("../packages/lib/32bit-compat", dst="lib32"): - self.path("libalut.so") - self.path("libidn.so.11") - self.path("libopenal.so.1") - # self.path("libortp.so") - self.path("libuuid.so.1") - self.end_prefix("lib32") + # 32bit libs needed for voice + if self.prefix(src="../packages/lib/release/32bit-compat", alt_build="../packages/libraries/x86_64-linux/lib/release/32bit-compat", dst="lib32"): + # Vivox libs + self.path("libalut.so") + self.path("libidn.so.11") + self.path("libopenal.so.1") + # self.path("libortp.so") + self.path("libuuid.so.1") + self.end_prefix("lib32") ################################################################ diff --git a/install.xml b/install.xml index 7ce1fa21e..963a1b361 100644 --- a/install.xml +++ b/install.xml @@ -1341,23 +1341,23 @@ darwin md5sum - 917a60e1b18dd0f5e6afb555623339a4 + 8eb5462ea9d469bce4b0d22bd8f0c33b url - https://bitbucket.org/SingularityViewer/libraries/downloads/slvoice-4.6.0009.20030-darwin-20140312.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/slvoice-4.6.0009.20030-darwin-20140312-rpk.tar.bz2 linux md5sum - 01573510dce7f380f44e561ef2f3dd9f + 1c8643e27023f1d9b1523ca5794b7475 url - https://bitbucket.org/SingularityViewer/libraries/downloads/vivox-2.1.3010.6270-linux-20090309.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/vivox-2.1.3010.6270-linux-20090309-rpk.tar.bz2 linux64 md5sum - 01573510dce7f380f44e561ef2f3dd9f + 1c8643e27023f1d9b1523ca5794b7475 url - https://bitbucket.org/SingularityViewer/libraries/downloads/vivox-2.1.3010.6270-linux-20090309.tar.bz2 + https://bitbucket.org/SingularityViewer/libraries/downloads/vivox-2.1.3010.6270-linux-20090309-rpk.tar.bz2 windows diff --git a/scripts/automated_build_scripts/opensrc-build.sh b/scripts/automated_build_scripts/opensrc-build.sh deleted file mode 100755 index 568a1ba7f..000000000 --- a/scripts/automated_build_scripts/opensrc-build.sh +++ /dev/null @@ -1,472 +0,0 @@ -#!/bin/sh - -# This is the build script used by Linden Lab's automated build system. -# - -set -x - -export INSTALL_USE_HTTP_FOR_SCP=true -export PATH=/bin:/usr/bin:$PATH -arch=`uname | cut -b-6` -here=`echo $0 | sed 's:[^/]*$:.:'` -# Hack : in the case of Snowglobe 1.x trunk and releases, we continue to use 2009 as the year so to separate them from Snowglobe 2.x trunk and releases -#year=`date +%Y` -year="2009" -branch=`svn info | grep '^URL:' | sed 's:.*/::'` -revision=`svn info | grep '^Revision:' | sed 's/.*: //'` -top=`cd "$here/../../.." && pwd` - -[ x"$WGET_CACHE" = x ] && export WGET_CACHE=/var/tmp/parabuild/wget -[ x"$S3GET_URL" = x ] && export S3GET_URL=http://viewer-source-downloads.s3.amazonaws.com/$year -[ x"$S3PUT_URL" = x ] && export S3PUT_URL=https://s3.amazonaws.com/viewer-source-downloads/$year -[ x"$S3SYMBOL_URL" = x ] && export S3SYMBOL_URL=https://s3.amazonaws.com/automated-builds-secondlife-com/binaries -[ x"$PUBLIC_URL" = x ] && export PUBLIC_URL=http://secondlife.com/developers/opensource/downloads/$year -[ x"$PUBLIC_EMAIL" = x ] && export PUBLIC_EMAIL=sldev-commits@lists.secondlife.com - -# Make sure command worked and bail out if not, reporting failure to parabuild -fail() -{ - echo "BUILD FAILED" $@ - exit 1 -} - -pass() -{ - echo "BUILD SUCCESSFUL" - exit 0 -} - -# Locking to avoid contention with u-s-c -LOCK_PROCESS= - -locking_available() -{ - test -n "$LOCK_CREATE" -a -x "$LOCK_CREATE"\ - -a -n "$LOCK_TOUCH" -a -x "$LOCK_TOUCH"\ - -a -n "$LOCK_REMOVE" -a -x "$LOCK_REMOVE" -} - -acquire_lock() -{ - if locking_available - then - if "$LOCK_CREATE" /var/lock/update-system-config --retry 99 - then - "$LOCK_TOUCH" /var/lock/update-system-config & - LOCK_PROCESS="$!" - else - fail acquire lock - fi - else - true - fi -} - -release_lock() -{ - if locking_available - then - if test x"$LOCK_PROCESS" != x - then - kill "$LOCK_PROCESS" - "$LOCK_REMOVE" /var/lock/update-system-config - else - echo No Lock Acquired >&2 - fi - else - true - fi -} - -get_asset() -{ - mkdir -p "$WGET_CACHE" || fail creating WGET_CACHE - local tarball=`basename "$1"` - test -r "$WGET_CACHE/$tarball" || ( cd "$WGET_CACHE" && curl --location --remote-name "$1" || fail getting $1 ) - case "$tarball" in - *.zip) unzip -qq -d "$top" -o "$WGET_CACHE/$tarball" || fail unzip $tarball ;; - *.tar.gz|*.tgz) tar -C "$top" -xzf "$WGET_CACHE/$tarball" || fail untar $tarball ;; - *) fail unrecognized filetype: $tarball ;; - esac -} - -s3_available() -{ - test -x "$helpers/hg/bin/s3get.sh" -a -x "$helpers/hg/bin/s3put.sh" -a -r "$helpers/hg/bin/s3curl.py" -} - -build_dir_Darwin() -{ - echo build-darwin-i386 -} - -build_dir_Linux() -{ - echo viewer-linux-i686-`echo $1 | tr A-Z a-z` -} - -build_dir_CYGWIN() -{ - echo build-vc80 -} - -installer_Darwin() -{ - ls -1td "$(build_dir_Darwin Release)/newview/"*.dmg 2>/dev/null | sed 1q -} - -installer_Linux() -{ - ls -1td "$(build_dir_Linux Release)/newview/"*.tar.bz2 2>/dev/null | sed 1q -} - -installer_CYGWIN() -{ - d=$(build_dir_CYGWIN Release) - p=$(sed 's:.*=::' "$d/newview/Release/touched.bat") - echo "$d/newview/Release/$p" -} - -# deal with aborts etc.. -trap fail 1 2 3 14 15 - -# Check location -cd "$here/../.." - -test -x ../linden/scripts/automated_build_scripts/opensrc-build.sh\ - || fail 'The parent dir of your checkout needs to be named "linden"' - -. doc/asset_urls.txt -get_asset "$SLASSET_ART" - -update_version_files= - -# Set up platform specific stuff -case "$arch" in - -# Note that we can only build the "Release" variant for Darwin, because of a compiler bug: -# ld: bl out of range (-16777272 max is +/-16M) -# from __static_initialization_and_destruction_0(int, int)at 0x033D319C -# in __StaticInit of -# indra/build-darwin-universal/newview/SecondLife.build/Debug/Second Life.build/Objects-normal/ppc/llvoicevisualizer.o -# to ___cxa_atexit$island_2 at 0x023D50F8 -# in __text of -# indra/build-darwin-universal/newview/SecondLife.build/Debug/Second Life.build/Objects-normal/ppc/Second Life -# in __static_initialization_and_destruction_0(int, int) -# from indra/build-darwin-universal/newview/SecondLife.build/Debug/Second Life.build/Objects-normal/ppc/llvoicevisualizer.o - -Darwin) - helpers=/usr/local/buildscripts/shared/latest - variants="Release" - cmake_generator="Xcode" - fmod=fmodapi375mac - fmod_tar="$fmod.zip" - fmod_so=libfmod.a - fmod_lib=lib - target_dirs="libraries/universal-darwin/lib_debug - libraries/universal-darwin/lib_release - libraries/universal-darwin/lib_release_client" - other_archs="$S3GET_URL/$branch/$revision/CYGWIN $S3GET_URL/$branch/$revision/Linux" - symbolfiles= - mail="$helpers"/mail.py - all_done="$helpers"/all_done.py - test -r "$helpers/update_version_files.py" && update_version_files="$helpers/update_version_files.py" - libs_asset="$SLASSET_LIBS_DARWIN" - s3put="$helpers"/hg/bin/s3put.sh - ;; - -CYGWIN) - helpers=/cygdrive/c/buildscripts/shared/latest - variants="Debug RelWithDebInfo Release ReleaseSSE2" - #variants="Release" - cmake_generator="vc80" - fmod=fmodapi375win - fmod_tar=fmodapi375win.zip - fmod_so=fmodvc.lib - fmod_lib=lib - target_dirs="libraries/i686-win32/lib/debug - libraries/i686-win32/lib/release" - other_archs="$S3GET_URL/$branch/$revision/Darwin $S3GET_URL/$branch/$revision/Linux" - symbolfiles="newview/Release/secondlife-bin.pdb newview/Release/secondlife-bin.map newview/Release/secondlife-bin.exe" - export PATH="/cygdrive/c/Python25:/cygdrive/c/Program Files/Cmake 2.6/bin":$PATH - export PERL="/cygdrive/c/Perl/bin/perl.exe" - export S3CURL="C:\\buildscripts\\shared\\latest\\hg\\bin\\s3curl.py" - export SIGN_PY="C:\\buildscripts\\shared\\latest\\code-signing\\sign.py" - export CURL="C:\\cygwin\\bin\\curl.exe" - mail="C:\\buildscripts\\shared\\latest\\mail.py" - all_done="C:\\buildscripts\\shared\\latest\\all_done.py" - test -r "$helpers/update_version_files.py" && update_version_files="C:\\buildscripts\\shared\\latest\\update_version_files.py" - libs_asset="$SLASSET_LIBS_WIN32" - s3put="$helpers"/hg/bin/s3put.sh - ;; - -Linux) - helpers=/var/opt/parabuild/buildscripts/shared/latest - if [ x"$CXX" = x ] - then - if test -x /usr/bin/g++-4.1 - then - if test -x /usr/bin/distcc - then - export CXX="/usr/bin/distcc /usr/bin/g++-4.1" - else - export CXX=/usr/bin/g++-4.1 - fi - fi - fi - variants="Debug RelWithDebInfo Release ReleaseSSE2" - #variants="Release" - cmake_generator="Unix Makefiles" - fmod=fmodapi375linux - fmod_tar="$fmod".tar.gz - fmod_so=libfmod-3.75.so - fmod_lib=. - target_dirs="libraries/i686-linux/lib_debug - libraries/i686-linux/lib_release - libraries/i686-linux/lib_release_client" - other_archs="$S3GET_URL/$branch/$revision/Darwin $S3GET_URL/$branch/$revision/CYGWIN" - symbolfiles= - mail="$helpers"/mail.py - all_done="$helpers"/all_done.py - test -r "$helpers/update_version_files.py" && update_version_files="$helpers/update_version_files.py" - # Change the DISTCC_DIR to be somewhere that the parabuild process can write to - if test -r /etc/debian_version - then - [ x"$DISTCC_DIR" = x ] && export DISTCC_DIR=/var/tmp/parabuild - #case `cat /etc/debian_version` in - #3.*) [ x"$DISTCC_HOSTS" = x ]\ - # && export DISTCC_HOSTS="build-linux-1/3 - # station30/2,lzo" ;; - #4.*) [ x"$DISTCC_HOSTS" = x ]\ - # && export DISTCC_HOSTS="build-linux-6/2,lzo - # build-linux-2/2,lzo - # build-linux-3/2,lzo - # build-linux-4/2,lzo - # build-linux-5/2,lzo - # build-linux-7/2,lzo - # build-linux-8/2,lzo - # build-linux-9/2,lzo" ;; - #esac - # Temp fix for Linux so that parabuild passes: use the new Linux build farm - export hostname=`hostname -f` - export phx_DISTCC_HOSTS="build-linux0.phx.lindenlab.com/2 build-linux1.phx.lindenlab.com/2 build-linux2.phx.lindenlab.com/2 build-linux3.phx.lindenlab.com/2 build-linux5.phx.lindenlab.com/2 build-linux5.phx.lindenlab.com/2 build-linux6.phx.lindenlab.com/2 " - export dfw_DISTCC_HOSTS="build-linux7.dfw.lindenlab.com/2 build-linux8.dfw.lindenlab.com/2 build-linux9.dfw.lindenlab.com/2 build-linux10.dfw.lindenlab.com/2 build-linux11.dfw.lindenlab.com/2 build-linux12.dfw.lindenlab.com/2 build-linux13.dfw.lindenlab.com/2 build-linux14.dfw.lindenlab.com/2 build-linux15.dfw.lindenlab.com/2" - case "$hostname" in - *.dfw.*) export DISTCC_HOSTS="$dfw_DISTCC_HOSTS" ;; - *.phx.*) export DISTCC_HOSTS="$phx_DISTCC_HOSTS" ;; - esac - fi - - libs_asset="$SLASSET_LIBS_LINUXI386" - s3put="$helpers"/hg/bin/s3put.sh - ;; - -*) fail undefined $arch ;; -esac - -acquire_lock -trap release_lock EXIT - -get_asset "http://www.fmod.org/files/fmod3/$fmod_tar" - -case "$arch" in - -Darwin) - # Create fat binary on Mac... - if lipo -create -output "../$fmod"/api/$fmod_lib/libfmod-universal.a\ - "../$fmod"/api/$fmod_lib/libfmod.a\ - "../$fmod"/api/$fmod_lib/libfmodx86.a - then - mv "../$fmod"/api/$fmod_lib/libfmod.a "../$fmod"/api/$fmod_lib/libfmodppc.a - mv "../$fmod"/api/$fmod_lib/libfmod-universal.a "../$fmod"/api/$fmod_lib/libfmod.a - echo Created fat binary - else - fail running lipo - fi - ;; - -CYGWIN) - # install Quicktime. This will fail outside of Linden's network - scripts/install.py quicktime - ;; - -esac - -# Only run this if the script exists -if test x"$update_version_files" = x -then - echo "Private Build..." > indra/build.log - [ x"$VIEWER_CHANNEL" = x ] && export VIEWER_CHANNEL="CommunityDeveloper" -else - # By right, this should be in the branched source tree, but for now it will be a helper - python "$update_version_files" --verbose --src-root=. --viewer > indra/build.log - [ x"$VIEWER_CHANNEL" = x ] && export VIEWER_CHANNEL="Snowglobe Test Build" -fi - -# First, go into the directory where the code was checked out by Parabuild -cd indra - -# This is the way it works now, but it will soon work on a variant dependent way -for target_dir in $target_dirs -do - mkdir -p "../$target_dir" - cp -f "../../$fmod/api/$fmod_lib/$fmod_so" "../$target_dir" -done -mkdir -p "../libraries/include" -cp -f "../../$fmod/api/inc/"* "../libraries/include" - -# Special Windows case -test -r "../../$fmod/api/fmod.dll" && cp -f "../../$fmod/api/fmod.dll" newview - -# Now run the build command over all variants -succeeded=true - -### TEST CODE - remove when done -### variants= -### echo "Artificial build failure to test notifications" > build.log -### succeeded=false -### END TEST CODE - -for variant in $variants -do - build_dir=`build_dir_$arch $variant` - rm -rf "$build_dir" - get_asset "$libs_asset" # This plunks stuff into the build dir, so have to restore it now. - - # SNOW-713 : hack around a Darwin lib 1.23.4.0 tarball issue introduced by the move from universal to i386 - # Should be removed when libs are rebuilt cleanly - if test -r build-darwin-universal-Release - then - mv build-darwin-universal-Release/ "$build_dir/" - fi - # End SNOW-713 hack - - # This is the way it will work in future - #for target_dir in $target_dirs - #do - # mkdir -p "$build_dir/$target_dir" - # cp "../../$fmod/api/$fmod_lib/$fmod_so" "$build_dir/$target_dir" - #done - #mkdir -p "$build_dir/libraries/include" - #cp "../../$fmod/api/inc/"* "$build_dir/libraries/include" - echo "==== $variant ====" >> build.log - if ./develop.py \ - --unattended \ - --incredibuild \ - -t $variant \ - -G "$cmake_generator" \ - configure \ - -DVIEWER_CHANNEL:STRING="$VIEWER_CHANNEL"\ - -DVIEWER_LOGIN_CHANNEL:STRING="$VIEWER_CHANNEL"\ - -DPACKAGE:BOOL=ON >>build.log 2>&1 - then - if ./develop.py\ - --unattended\ - --incredibuild \ - -t $variant\ - -G "$cmake_generator" \ - build prepare >>build.log 2>&1 - then - if ./develop.py\ - --unattended\ - --incredibuild \ - -t $variant\ - -G "$cmake_generator" \ - build package >>build.log 2>&1 - then - # run tests if needed - true - else - succeeded=false - fi - else - succeeded=false - fi - else - succeeded=false - fi -done - -# Check status and upload results to S3 -subject= -if $succeeded -then - package=`installer_$arch` - test -r "$package" || fail not found: $package - package_file=`echo $package | sed 's:.*/::'` - if s3_available - then - # Create an empty token file and populate it with the usable URLs: this will be emailed when all_done... - cp /dev/null "$arch" - echo "$PUBLIC_URL/$branch/$revision/$package_file" >> "$arch" - echo "$PUBLIC_URL/$branch/$revision/good-build.$arch" >> "$arch" - "$s3put" "$package" "$S3PUT_URL/$branch/$revision/$package_file" binary/octet-stream public-read\ - || fail Uploading "$package" - "$s3put" build.log "$S3PUT_URL/$branch/$revision/good-build.$arch" text/plain public-read\ - || fail Uploading build.log - "$s3put" "$arch" "$S3PUT_URL/$branch/$revision/$arch" text/plain public-read\ - || fail Uploading token file - for symbolfile in $symbolfiles - do - targetfile="`echo $symbolfile | sed 's:.*/::'`" - "$s3put" "$build_dir/$symbolfile" "$S3SYMBOL_URL/$revision/$targetfile" binary/octet-stream public-read\ - || fail Uploading "$symbolfile" - done - if python "$all_done"\ - curl\ - "$S3GET_URL/$branch/$revision/$arch"\ - $other_archs > message - then - subject="Successful Build for $year/$branch ($revision)" - fi - else - true s3 is not available - fi -else - if s3_available - then - "$s3put" build.log "$S3PUT_URL/$branch/$revision/failed-build.$arch" text/plain public-read\ - || fail Uploading build.log - subject="Failed Build for $year/$branch ($revision) on $arch" - cat >message <> message - elif [ x"$PARABUILD_PREVIOUS_CHANGE_LIST_NUMBER" = x ] - then - ( cd .. && svn log --verbose --stop-on-copy --limit 50 ) >> message - else - if [ "$PARABUILD_PREVIOUS_CHANGE_LIST_NUMBER" -lt "$PARABUILD_CHANGE_LIST_NUMBER" ] - then - range=`expr 1 + "$PARABUILD_PREVIOUS_CHANGE_LIST_NUMBER"`:"$PARABUILD_CHANGE_LIST_NUMBER" - else - range="$PARABUILD_CHANGE_LIST_NUMBER" - fi - ( cd .. && svn log --verbose -r"$range" ) >> message - fi - # $PUBLIC_EMAIL can be a list, so no quotes - python "$mail" "$subject" $PUBLIC_EMAIL < message -fi - -if $succeeded -then - pass -else - fail -fi - diff --git a/scripts/public_fetch_tarballs.py b/scripts/public_fetch_tarballs.py deleted file mode 100755 index 45eb4c5a8..000000000 --- a/scripts/public_fetch_tarballs.py +++ /dev/null @@ -1,280 +0,0 @@ -#!/usr/bin/python -"""\ -@file public_fetch_tarballs.py -@author Rob Lanphier -@date 2009-05-30 -@brief Fetch + extract tarballs and zipfiles listed in doc/asset_urls.txt - -$LicenseInfo:firstyear=2009&license=viewergpl$ -Copyright (c) 2009, Linden Research, Inc. - -Second Life Viewer Source Code -The source code in this file ("Source Code") is provided by Linden Lab -to you under the terms of the GNU General Public License, version 2.0 -("GPL"), unless you have obtained a separate licensing agreement -("Other License"), formally executed by you and Linden Lab. Terms of -the GPL can be found in doc/GPL-license.txt in this distribution, or -online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - -There are special exceptions to the terms and conditions of the GPL as -it is applied to this Source Code. View the full text of the exception -in the file doc/FLOSS-exception.txt in this software distribution, or -online at -http://secondlifegrid.net/programs/open_source/licensing/flossexception - -By copying, modifying or distributing this software, you acknowledge -that you have read and understood your obligations described above, -and agree to abide by those obligations. - -ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -COMPLETENESS OR PERFORMANCE. -$/LicenseInfo$ -""" - -import sys -import os.path - -# Look for indra/lib/python in all possible parent directories ... -# This is an improvement over the setup-path.py method used previously: -# * the script may blocated anywhere inside the source tree -# * it doesn't depend on the current directory -# * it doesn't depend on another file being present. - -def add_indra_lib_path(): - root = os.path.realpath(__file__) - # always insert the directory of the script in the search path - dir = os.path.dirname(root) - if dir not in sys.path: - sys.path.insert(0, dir) - - # Now go look for indra/lib/python in the parent dies - while root != os.path.sep: - root = os.path.dirname(root) - dir = os.path.join(root, 'indra', 'lib', 'python') - if os.path.isdir(dir): - if dir not in sys.path: - sys.path.insert(0, dir) - return root - else: - print >>sys.stderr, "This script is not inside a valid installation." - sys.exit(1) - -base_dir = add_indra_lib_path() -print base_dir - -import os -import sys -import re -import urllib -import zipfile -import tarfile -import optparse -import tempfile - -import indra.util.helpformatter - -# load + parse doc/asset_urls.txt -def get_asset_urls(): - asset_urls={} - f = open(os.path.join(base_dir,"doc", "asset_urls.txt")) - for line in f: - line=line.strip() - (name, value)=re.split("=", line, 1) - asset_urls[name]=value - return asset_urls - -# Filename from a URL -def get_asset_filename_from_url(asseturl, targetdir): - i = asseturl.rfind('/') - filename = os.path.join(targetdir, asseturl[i+1:]) - return filename - - -# Extract .zip file to targetdir. Called by extract_archive_sans_linden. -def extract_zipfile_sans_linden(filename, targetdir): - archive = zipfile.ZipFile(filename, 'r') - names = archive.namelist() - for path in names: - if(path=="linden/"): - pass - target = os.path.join(targetdir, re.sub("linden/", "", path)) - subdir = os.path.dirname(target) - if not os.path.exists(subdir): - os.makedirs(subdir) - if not os.path.exists(target): - fp = open(target, 'wb') - fp.write(archive.read(path)) - fp.close() - archive.close() - - -# Extract .tar.gz file to targetdir. Called by extract_archive_sans_linden. -def extract_tarball_sans_linden(filename, targetdir): - archive = tarfile.TarFile.open(filename, 'r') - # get a series of TarInfo objects - tarentries=archive.getmembers() - for tarentry in tarentries: - target = re.sub(r'^(\./)?(linden/?)?', "", tarentry.name) - if(target==""): - continue - fulltarget=os.path.join(targetdir, target) - subdir = os.path.dirname(fulltarget) - if not os.path.exists(subdir): - os.makedirs(subdir) - if not os.path.exists(fulltarget): - # Reset the name property on the TarInfo object, so it writes the - # file exactly where we want it. It's hard telling for sure if this - # property is intended to be written to, but it works for now. - tarentry.name=fulltarget - # Calling TarFile.extract with the "path" parameter doesn't work as - # we might hope, because the path components in the tarball get - # appended to the "path" parameter. Hence the reason for passing in - # the TarInfo object with the munged name property - archive.extract(tarentry) - archive.close() - -# Extract either .tar.gz file or .zip file to targetdir, stripping off the -# leading "linden" directory, but leaving the directory structure otherwise -# intact. -def extract_archive_sans_linden(filename, targetdir): - if(filename.endswith('.tar.gz')): - extract_tarball_sans_linden(filename, targetdir) - elif(filename.endswith('.zip')): - extract_zipfile_sans_linden(filename, targetdir) - else: - raise Exception, "Unhandled archive type" - -def get_assetnames_by_platform(platform): - assetnames=['SLASSET_ART'] - if(platform=='linux' or platform=='all'): - assetnames.append('SLASSET_LIBS_LINUXI386') - if(platform=='darwin' or platform=='all'): - assetnames.append('SLASSET_LIBS_DARWIN') - if(platform=='windows' or platform=='all'): - assetnames.append('SLASSET_LIBS_WIN32') - return assetnames - -# adapted from install.py -def _get_platform(): - "Return appropriate platform packages for the environment." - platform_map = { - 'darwin': 'darwin', - 'linux2': 'linux', - 'win32' : 'windows', - 'cygwin' : 'windows', - 'solaris' : 'solaris' - } - this_platform = platform_map[sys.platform] - return this_platform - -# copied from install.py -def _default_installable_cache(): - """In general, the installable files do not change much, so find a - host/user specific location to cache files.""" - user = _getuser() - cache_dir = "/var/tmp/%s/sg.install.cache" % user - if _get_platform() == 'windows': - cache_dir = os.path.join(tempfile.gettempdir(), \ - 'sg.install.cache.%s' % user) - return cache_dir - -# For status messages (e.g. "Loading..."). May come in handy if -# we implement a "quiet" mode. -def _report(string): - print string - - -# copied from install.py -def _getuser(): - "Get the user" - try: - # Unix-only. - import getpass - return getpass.getuser() - except ImportError: - import win32api - return win32api.GetUserName() - -# adapted from install.py -def _parse_args(): - parser = optparse.OptionParser( - usage="usage: %prog [options]", - formatter = indra.util.helpformatter.Formatter(), - description="""This script fetches and installs tarballs and \ -zipfiles ("asset bundles") listed in doc/asset_urls.txt - -If no asset bundles are specified on the command line, then the default \ -behavior is to install all known asset bundles appropriate for the platform \ -specified. You can specify more than one asset bundle on the command line. - -Example: - %prog SLASSET_ART - - This looks for the "SLASSET_ART" entry in doc/asset_urls.txt, and extracts - the corresponding asset bundle into your source tree. -""") - parser.add_option( - '-p', '--platform', - type='choice', - default=_get_platform(), - dest='platform', - choices=['windows', 'darwin', 'linux', 'solaris', 'all'], - help="""Override the automatically determined platform. \ -You can specify 'all' to get assets for all platforms. Choices: windows, \ -darwin, linux, solaris, or all. Default: autodetected (%s)""" % \ - _get_platform()) - parser.add_option( - '--cache-dir', - type='string', - default=_default_installable_cache(), - dest='cache_dir', - help='Where to download files. Default: %s'% \ - (_default_installable_cache())) - parser.add_option( - '--install-dir', - type='string', - default=base_dir, - dest='install_dir', - help='Where to unpack the installed files. Default: %s' % base_dir) - - return parser.parse_args() - -def main(argv): - options, args = _parse_args() - # 1. prepare cache dir - if not os.path.exists(options.cache_dir): - os.makedirs(options.cache_dir) - - # 2. read doc/asset_urls.txt - asseturls=get_asset_urls() - - # 3. figure out which asset bundles we'll be downloading - if len(args)>0: - assetnames=args - else: - assetnames=get_assetnames_by_platform(options.platform) - - # 4. download and extract each asset bundle - for asset in assetnames: - # 4a. get the URL for the asset bundle - try: - asseturl=asseturls[asset] - except: - print "No asset in doc/asset_urls.txt named %s" % asset - sys.exit(2) - # 4b. figure out where to put the downloaded asset bundle - filename=get_asset_filename_from_url(asseturl, options.cache_dir) - - # 4c. see if we have it, and if not, get it - if os.path.exists(filename): - _report("Using already downloaded "+filename+" ...") - else: - _report("Downloading "+filename+" ...") - urllib.urlretrieve(asseturl, filename) - # 4d. extract it into the tree - extract_archive_sans_linden(filename, options.install_dir) - -# execute main() only if invoked directly: -if __name__ == "__main__": - sys.exit(main(sys.argv)) From 5c5fae78c6e61f0b2771269f5995aaa9ad31875f Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 18 Dec 2014 18:08:59 -0600 Subject: [PATCH 32/55] Fix some annoying issues with spaces in develop.py, for windows. --- indra/develop.py | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/indra/develop.py b/indra/develop.py index 94c8db5ea..f7bac3edd 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -41,6 +41,7 @@ import shutil import socket import sys import commands +import shlex class CommandError(Exception): pass @@ -506,6 +507,11 @@ class WindowsSetup(PlatformSetup): generator = property(_get_generator, _set_generator) + def get_gen_str(self, gen): + if gen is None: + gen = self._generator + return self.gens[gen.lower()]['ver'] + def os(self): return 'win32' @@ -539,7 +545,7 @@ class WindowsSetup(PlatformSetup): '-DSTANDALONE:BOOL=%(standalone)s ' '-DUNATTENDED:BOOL=%(unattended)s ' '-DWORD_SIZE:STRING=%(word_size)s ' - '-DROOT_PROJECT_NAME:STRING=%(project_name)s ' + '-DROOT_PROJECT_NAME:STRING=\"%(project_name)s\" ' '%(opts)s "%(dir)s"' % args) def get_HKLM_registry_value(self, key_str, value_str): @@ -551,19 +557,17 @@ class WindowsSetup(PlatformSetup): return value def find_visual_studio(self, gen=None): - if gen is None: - gen = self._generator - gen = gen.lower() + gen = self.get_gen_str(gen) value_str = (r'EnvironmentDirectory') key_str = (r'SOFTWARE\Microsoft\VisualStudio\%s\Setup\VS' % - self.gens[gen]['ver']) + gen) print ('Reading VS environment from HKEY_LOCAL_MACHINE\%s\%s' % (key_str, value_str)) try: return self.get_HKLM_registry_value(key_str, value_str) except WindowsError, err: key_str = (r'SOFTWARE\Wow6432Node\Microsoft\VisualStudio\%s\Setup\VS' % - self.gens[gen]['ver']) + gen) try: return self.get_HKLM_registry_value(key_str, value_str) @@ -571,14 +575,13 @@ class WindowsSetup(PlatformSetup): print >> sys.stderr, "Didn't find ", self.gens[gen]['gen'] return '' + def find_visual_studio_express(self, gen=None): - if gen is None: - gen = self._generator - gen = gen.lower() + gen = self.get_gen_str(gen) try: import _winreg key_str = (r'SOFTWARE\Microsoft\VCEXpress\%s\Setup\VC' % - self.gens[gen]['ver']) + gen) value_str = (r'ProductDir') print ('Reading VS environment from HKEY_LOCAL_MACHINE\%s\%s' % (key_str, value_str)) @@ -590,17 +593,15 @@ class WindowsSetup(PlatformSetup): print 'Found: %s' % value return value except WindowsError, err: - print >> sys.stderr, "Didn't find ", self.gens[gen]['gen'] + print >> sys.stderr, "Didn't find ", gen return '' def find_visual_studio_express_single(self, gen=None): - if gen is None: - gen = self._generator - gen = gen.lower() + gen = self.get_gen_str(gen) try: import _winreg key_str = (r'SOFTWARE\Microsoft\VCEXpress\%s_Config\Setup\VC' % - self.gens[gen]['ver']) + gen) value_str = (r'ProductDir') print ('Reading VS environment from HKEY_CURRENT_USER\%s\%s' % (key_str, value_str)) @@ -612,7 +613,7 @@ class WindowsSetup(PlatformSetup): print 'Found: %s' % value return value except WindowsError, err: - print >> sys.stderr, "Didn't find ", self.gens[gen]['gen'] + print >> sys.stderr, "Didn't find ", gen return '' def get_build_cmd(self): @@ -621,7 +622,7 @@ class WindowsSetup(PlatformSetup): if self.gens[self.generator]['ver'] in [ r'8.0', r'9.0' ]: config = '\"%s|Win32\"' % config - return "buildconsole %s.sln /build %s" % (self.project_name, config) + return "buildconsole \"%s.sln\" /build %s" % (self.project_name, config), None environment = self.find_visual_studio() if environment == '': environment = self.find_visual_studio_express() @@ -637,15 +638,16 @@ class WindowsSetup(PlatformSetup): exit(0) # devenv.com is CLI friendly, devenv.exe... not so much. - return ('"%sdevenv.com" %s.sln /build %s' % + return ('"%sdevenv.com" \"%s.sln\" /build %s' % (self.find_visual_studio(), self.project_name, self.build_type)) def run(self, command, name=None): '''Run a program. If the program fails, raise an exception.''' - ret = os.system(command) + ret = os.system('\"'+command+'\"') if ret: if name is None: - name = command.split(None, 1)[0] + name = os.path.normpath(shlex.split(command.encode('utf8'),posix=False)[0].strip('"')) + path = self.find_in_path(name) if not path: ret = 'was not found' @@ -677,9 +679,9 @@ class WindowsSetup(PlatformSetup): else: tool_path = os.path.join('indra','tools','vstool','VSTool.exe') vstool_cmd = (tool_path + - ' --solution ' + - os.path.join(build_dir,'Singularity.sln') + - ' --config ' + self.build_type + + ' --solution \"' + + os.path.join(build_dir,'%s.sln' % self.project_name) + + '\" --config ' + self.build_type + ' --startup secondlife-bin') print 'Running vstool %r in %r' % (vstool_cmd, getcwd()) self.run(vstool_cmd) From e57bcea3b648bc13ceeb1a5b53a39d27cf103c30 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 18 Dec 2014 18:26:21 -0600 Subject: [PATCH 33/55] Experimentation with msbuild 12.0 --- indra/cmake/00-Common.cmake | 2 +- indra/develop.py | 43 ++++++++++++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index cc6f145aa..973111b8d 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -91,7 +91,7 @@ if (WINDOWS) # SSE2 is implied on win64 if(WORD_SIZE EQUAL 32) - add_definitions(/arch:SSE2) + add_definitions(/arch:SSE2 /D_ATL_XP_TARGETING) else(WORD_SIZE EQUAL 32) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /wd4267 /wd4250 /wd4244") endif(WORD_SIZE EQUAL 32) diff --git a/indra/develop.py b/indra/develop.py index f7bac3edd..8e1dc4150 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -574,7 +574,29 @@ class WindowsSetup(PlatformSetup): except: print >> sys.stderr, "Didn't find ", self.gens[gen]['gen'] return '' - + + def find_msbuild(self, gen=None): + gen = self.get_gen_str(gen) + + key_str = (r'SOFTWARE\Microsoft\MSBuild\ToolsVersions\12.0') + + print ('Checking MSBuild support for vs ver = %s' % gen) + if not self.get_HKLM_registry_value(key_str+'\\'+gen, "VCTargetsPath"): + return (None, None) + print ('Reading MSBuild location from HKEY_LOCAL_MACHINE\%s\MSBuildToolsPath' % + key_str) + print key_str + try: + return (self.get_HKLM_registry_value(key_str, 'MSBuildToolsPath'), gen) + except WindowsError, err: + key_str = (r'SOFTWARE\Wow6432Node\Microsoft\MSBuild\ToolsVersions\%s' % + gen) + + try: + return (self.get_HKLM_registry_value(key_str, 'MSBuildToolsPath'), gen) + except WindowsError, err: + print 'Didn\'t find msbuild' + return (None, None) def find_visual_studio_express(self, gen=None): gen = self.get_gen_str(gen) @@ -637,9 +659,15 @@ class WindowsSetup(PlatformSetup): print >> sys.stderr, "\nPlease see https://wiki.secondlife.com/wiki/Microsoft_Visual_Studio#Extra_steps_for_Visual_Studio_Express_editions for Visual Studio Express specific information" exit(0) + msbuild_dir, tool_ver = self.find_msbuild() + + if msbuild_dir is not None and tool_ver is not None: + return ('\"%smsbuild.exe\" \"%s.sln\" /p:configuration=%s /p:VisualStudioVersion=%s' % + (msbuild_dir, self.project_name, self.build_type, tool_ver)), True + # devenv.com is CLI friendly, devenv.exe... not so much. return ('"%sdevenv.com" \"%s.sln\" /build %s' % - (self.find_visual_studio(), self.project_name, self.build_type)) + (self.find_visual_studio(), self.project_name, self.build_type)), None def run(self, command, name=None): '''Run a program. If the program fails, raise an exception.''' @@ -689,16 +717,21 @@ class WindowsSetup(PlatformSetup): def run_build(self, opts, targets): cwd = getcwd() - build_cmd = self.get_build_cmd() + build_cmd, msbuild = self.get_build_cmd() for d in self.build_dirs(): try: os.chdir(d) if targets: - for t in targets: - cmd = '%s /project %s %s' % (build_cmd, t, ' '.join(opts)) + if msbuild: + cmd = '%s /target:%s %s' % (build_cmd, ';'.join(targets), ' '.join(opts)) print 'Running build(targets) %r in %r' % (cmd, d) self.run(cmd) + else: + for t in targets: + cmd = '%s /project %s %s' % (build_cmd, t, ' '.join(opts)) + print 'Running build(targets) %r in %r' % (cmd, d) + self.run(cmd) else: cmd = '%s %s' % (build_cmd, ' '.join(opts)) print 'Running build %r in %r' % (cmd, d) From 08db85fa99fce840096de6181d1e0c42ce35fdbc Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 18 Dec 2014 23:17:03 -0600 Subject: [PATCH 34/55] Unused assets can DIAF. --- indra/cmake/BuildVersion.cmake | 5 - indra/lib/python/indra/util/llversion.py | 95 ----------- indra/newview/CMakeLists.txt | 4 +- indra/newview/res-sdl/ll_icon.BMP | Bin 5174 -> 0 bytes indra/newview/res-sdl/snowglobe_icon.BMP | Bin 5170 -> 0 bytes indra/newview/res/Ascent_icon.png | Bin 37451 -> 0 bytes indra/newview/res/icon1.ico | Bin 766 -> 0 bytes indra/newview/res/ll_icon.BMP | Bin 262198 -> 0 bytes indra/newview/res/ll_icon.ico | Bin 364590 -> 0 bytes indra/newview/res/ll_icon.png | Bin 74846 -> 0 bytes indra/newview/res/singularity_icon.bmp | Bin 262198 -> 0 bytes indra/newview/res/singularity_icon_bc.ico | Bin 5430 -> 0 bytes indra/newview/res/snowglobe_icon.BMP | Bin 262198 -> 0 bytes indra/newview/res/snowglobe_icon.ico | Bin 142520 -> 0 bytes indra/newview/res/snowglobe_icon.png | Bin 46123 -> 0 bytes indra/newview/res/viewerRes_bc.rc.in | 193 ---------------------- 16 files changed, 1 insertion(+), 296 deletions(-) delete mode 100644 indra/lib/python/indra/util/llversion.py delete mode 100644 indra/newview/res-sdl/ll_icon.BMP delete mode 100644 indra/newview/res-sdl/snowglobe_icon.BMP delete mode 100644 indra/newview/res/Ascent_icon.png delete mode 100644 indra/newview/res/icon1.ico delete mode 100644 indra/newview/res/ll_icon.BMP delete mode 100644 indra/newview/res/ll_icon.ico delete mode 100644 indra/newview/res/ll_icon.png delete mode 100644 indra/newview/res/singularity_icon.bmp delete mode 100644 indra/newview/res/singularity_icon_bc.ico delete mode 100644 indra/newview/res/snowglobe_icon.BMP delete mode 100644 indra/newview/res/snowglobe_icon.ico delete mode 100644 indra/newview/res/snowglobe_icon.png delete mode 100644 indra/newview/res/viewerRes_bc.rc.in diff --git a/indra/cmake/BuildVersion.cmake b/indra/cmake/BuildVersion.cmake index 2f983b261..54ae4b152 100644 --- a/indra/cmake/BuildVersion.cmake +++ b/indra/cmake/BuildVersion.cmake @@ -38,11 +38,6 @@ if (WINDOWS) ${CMAKE_SOURCE_DIR}/newview/res/viewerRes.rc.in ${CMAKE_SOURCE_DIR}/newview/res/viewerRes.rc ) - - configure_file( - ${CMAKE_SOURCE_DIR}/newview/res/viewerRes_bc.rc.in - ${CMAKE_SOURCE_DIR}/newview/res/viewerRes_bc.rc - ) endif (WINDOWS) if (DARWIN) diff --git a/indra/lib/python/indra/util/llversion.py b/indra/lib/python/indra/util/llversion.py deleted file mode 100644 index 770b861dd..000000000 --- a/indra/lib/python/indra/util/llversion.py +++ /dev/null @@ -1,95 +0,0 @@ -"""@file llversion.py -@brief Utility for parsing llcommon/llversion${server}.h - for the version string and channel string - Utility that parses svn info for branch and revision - -$LicenseInfo:firstyear=2006&license=mit$ - -Copyright (c) 2006-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import re, sys, os, commands - -# Methods for gathering version information from -# llversionviewer.h and llversionserver.h - -def get_src_root(): - indra_lib_python_indra_path = os.path.dirname(__file__) - return os.path.abspath(os.path.realpath(indra_lib_python_indra_path + "/../../../../../")) - -def get_version_file_contents(version_type): - filepath = get_src_root() + '/indra/llcommon/llversion%s.h' % version_type - file = open(filepath,"r") - file_str = file.read() - file.close() - return file_str - -def get_version(version_type): - file_str = get_version_file_contents(version_type) - m = re.search('const S32 LL_VERSION_MAJOR = (\d+);', file_str) - VER_MAJOR = m.group(1) - m = re.search('const S32 LL_VERSION_MINOR = (\d+);', file_str) - VER_MINOR = m.group(1) - m = re.search('const S32 LL_VERSION_PATCH = (\d+);', file_str) - VER_PATCH = m.group(1) - m = re.search('const S32 LL_VERSION_BUILD = (\d+);', file_str) - VER_BUILD = m.group(1) - version = "%(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s" % locals() - return version - -def get_channel(version_type): - file_str = get_version_file_contents(version_type) - m = re.search('const char \* const LL_CHANNEL = "(.+)";', file_str) - return m.group(1) - -def get_viewer_version(): - return get_version('viewer') - -def get_server_version(): - return get_version('server') - -def get_viewer_channel(): - return get_channel('viewer') - -def get_server_channel(): - return get_channel('server') - -# Methods for gathering subversion information -def get_svn_status_matching(regular_expression): - # Get the subversion info from the working source tree - status, output = commands.getstatusoutput('svn info %s' % get_src_root()) - m = regular_expression.search(output) - if not m: - print "Failed to parse svn info output, resultfollows:" - print output - raise Exception, "No matching svn status in "+src_root - return m.group(1) - -def get_svn_branch(): - branch_re = re.compile('URL: (\S+)') - return get_svn_status_matching(branch_re) - -def get_svn_revision(): - last_rev_re = re.compile('Last Changed Rev: (\d+)') - return get_svn_status_matching(last_rev_re) - - diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index af9604952..c9b21e390 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1228,7 +1228,6 @@ if (WINDOWS) res/arrowcopmulti.cur res/arrowdrag.cur res/circleandline.cur - res/icon1.ico res/llarrow.cur res/llarrowdrag.cur res/llarrowdragmulti.cur @@ -1248,8 +1247,7 @@ if (WINDOWS) res/lltooltranslate.cur res/lltoolzoomin.cur res/lltoolzoomout.cur - res/snowglobe_icon.BMP - res/snowglobe_icon.ico + res/${VIEWER_BRANDING_ID}_icon.ico res/resource.h res/toolpickobject.cur res/toolpickobject2.cur diff --git a/indra/newview/res-sdl/ll_icon.BMP b/indra/newview/res-sdl/ll_icon.BMP deleted file mode 100644 index 4a44aafbfaeefcc50b9a461693f9de9bc29b8930..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5174 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 diff --git a/indra/newview/res-sdl/snowglobe_icon.BMP b/indra/newview/res-sdl/snowglobe_icon.BMP deleted file mode 100644 index af6eab9fe59eaceec0f0d099fd533b80b9ef6666..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5170 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 diff --git a/indra/newview/res/Ascent_icon.png b/indra/newview/res/Ascent_icon.png deleted file mode 100644 index ac8444a63c5450ba7ae753a6aade5ff666dcc981..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37451 zcmX6_1z40#7kzhWq@}y0kuK>JX^?K|kVZO|E)fAKr8}ggK}t#l2`QyRq(r*wAHM%_ zUG{-p-gjp1+0`Oio(cXLh&UH=Sz6fcd(uY5Sl5Qb#*ilA%KbqjDpM(c)y|8Zz_S zKKQB?Egk{s*Dmg^04W3(-QX|;ljs->4+;52`UxxU1$_jE*XqwtMvGRQjj5@V+o^`_ zdvm*OOGYO*ny-{fwzJEPURAUjc^&#^UwJOI>XfKuJ=ybLJQLqrC6LFLM2IvSKVA=> zZWce8eE#iDUE+cfTJDW`Snffeh<+W^xt%_`fi!vqA^Q5{At_os2>MuET~J$#zvW3T zk%M~?Ky6x0A~qVfCVYK4An^x2hGH&+(1q}!7Z+SR1vl8xG^}uav~|Sn@Qxhu0g8lF zZvSjZa-|MgH!nE5Qbt+SqUVg>KWA@fK2BRX@zZ1>d^@y0wy+SN%&eZ@)I@T01g_rQ zEe&R!VVs=XBC}P9624E1kWiFc zM9a0V80e)Zc8jC?jIoRYxm-`v+|^3)h49&qlorB>8EW9}oDXbfEBf}AoX=rMV_jG) zLIMJGlsN1IH$jpC5;Tx@%%N2TGyT=}Hrr=YF5V0*f*53plNj}8h!WaQzxE}S@~jwm z5mo~?hq^E>j{2D;PFPlOw?kc4$O=E(ysPt_Ga?#I#QV*k68TmvqhTT7j_Y|b zw~0@wHFd8pE`0yaw>*TnMMPq~m=w`TyRhO10yaXn8vc(nf!qh)>x)`qLm15H7#Y4eC90wtk(;mMVctcgb z6nzPsv_W;zQ`iq2h4d?KA8>t!pl`A2=s6p{!^6WYexFA-dbYRAEsB^UKZ96Uy6H1J zMiRzcE`X@fx|~kxI{GwF_o*N=nCtb-ZBOvmB$|2^1h)lw_&zKb%!-inyhaCDAXupl zW-rULbO}f2X5*?RqKD@l))0r9`a_9d@9D$UJ9 zEiElKcXoPy{2(wiGU`4O)78^k@oG!_ZnJXWL%4dEk`Q3U%$R@23e7cLUM&7eN7?s= zIu2=WSGm-Z!@_HZ)<13kb`^fDB)>|MqJ*;UZjgurc71Vi@z~Fw(J3iJP|_003*?z< zGql`J`3tYytUX6msfUv7+h}3Z5&LOr_xlxG>g}KvVg6kc&5a$rD~b;P7Rl&FWvQeY z^BLQD&fP)h?F=h~hb;XHo(N}4!96UXgT`ktAA}gVq#cQypK6KIlDKGx_3@O*M#Md z@3PF!BTS)Aq-Vn$O_w4;5EC+_4m-cL?KtlflG6;QMPK%%2C*7S+qHI(tIY;J$JR*{ zCr=nB|4!+Vj~F$05rO~PT?8ajh7_fh2oh6`Yn-oQ@Wok`<*QiO7}xIKUv-!Vdtl>D zOiW{B1P;p+B-z+zc%rLASRP zX6G_RU~YE3de+_Zj33M5A%kKNAW>Z!H-FJ z7huccA^jBO9aO7{h^%OFC=R3 zK!q}XEWhX7w!lIh_pKQZ^{rY7J{D>sc*co(l>yO`p)Dz?EIWON>U;OjuJADDo}=U?ZD`qjgdqR)?-zsuh!az_`-!8r#R->BS6I=t)xyS^;BJytJ~*VN87 z!b1jk2OhtVeC6oxqX|?h5Lm91wqKDK9dKk_j8HWk*YA4KBsbyvJc<=TYr1E84{~PW zvUYbjcmMXDHVX0!@zW|+&LSUs*U43*z3-AowbY1~^PmniCX|2o&3c%FbLDc9P=Sfr z&C&tW_nW|(pbUAJEiB-m>GnKfh%pbt)$!_Wz^F~#vanjrkvR`+xpISjztIX(xSx-M zGW7rpNkk{)YE^Niw0z=I)80VT_C&O{q5#R{`*+tCV@%Dqg>7tfnR5j>Gxog5#Wo=e zWDa`-H_ZKY0JE(&8<7gvV+$*H0*2&d(yR>Wl~W7!H0XLq{bnntgElt*(~l#u1Q0Qb zNGqLQ8kJqRW;`kXfELD_0#I5x5P#)OJ$)7d^GYNJBe zll~0>Y2~@!2AMQJy;p3m&VVYo>>EWB2CZGy#~jQ3lY~`6Idi>u7s1eVKl9yYLUD{{ zjaFILx;tIk5FIJj7w{r^kQNW!+}GFO1-w^pxsnOvvlA&|x+g*Ung)3v1==lkwR!I;@DqkUXktqGK=1y(>pPxgU>21TB3rGR-Ulrm`VMXG z5ZX%nb9}t~Z_(6~Ux4+==hSnU0Ibe4NPN}S zF>G(RvaXNxZM=Jb1ELvL>M7gk}*lT4WXLFIP#lL#!5DL_JF(`RiAjxpU zREvC~1aF!nUNMS=gh1h-Fhrf#~(EgNEl-WqY$=Xb^;IJsfF)8^t@h*a3yooNq|&w|3yLRBWR ziAv|S7|j^V%t33ocl$&oj3IL&r_Nxb#_d0IS4*mLeDMO7bN7AenhXklqe^hmPK}GR zyBHsJy7!cyPz&8eIb8v(i{k)kJ0+(`Vr-0$aGUHL{UbN5t{g$eiU6Go;g@Q=(UeZx z2Pm2CenhrWk#E0m2(7(B(t7`C31xqv^A;_bmThkefiZT1>-?0-b&dy08;TeWI<0HX z@O}2gIVnYt>6(`D%iT|#w;FxYcRD3m+M1{wsk)l#juwPI&YYRdDXqpKK=T*$}IJx zFQVrKzkmR1b~wQGO$(>XPCd|Ztn}M(D!)OI-~(Bj*$2xsH5l}rOFMmmSEnl-?fyh& ze(#retRWk@JHuX=UP6cN*fg3QQk3%aIoM7isa=MesZ+#UC~VXE&lJP8IXCD(?lab^ zEv>;t7{xD1kSQ6^aF)mZE$sV=E&pcWeg6{{18XMh#Z|;YT#wLuwofg+{yCb&Vuux< zz<%}9kM=0HpbSHrZRhb&xr+{F=~L|7m~367V^Zkm=0`k*u$%33CTjmssf5%)u7^fFL~2E*%H52wSW}zgT)s^NfZ<~co2%* z`VC3Ro#7Z`Ms8|~Ft5LmVZRJtJSeSV-hZ%)iHQZ>TOqC89_FBRj~t_uAul=tMLBnj z`(V(5a=i#HJFEacsa72sbfbJWN`$umO0nfk2-!3N4R_HE?cZFJI-PMHmXu?C0U1J9 z+&tz^S2viw>dVGc1&4A7nMzgFE?VI`{-=HkDJf;xhd~Ijy@2GtxIV7=IpAPbVUH9v zS>l}+7o(%-p@^4)52W$5%Mo6Vt`)V`BLC{8MSBC&@a_PTmdV3|hd|(QyZcmf7Ij(v zCGEl9zwIxh?R$Zk2YhHMOhF8GF=kgEQloWtcepVasLWcJEjPM$y^P);*C{It>~(KP zWpw;Hd0r|o{n3r_=fYG(g4nI_zxfsq*-N@LHHhnG(B(ffe=MTuGzO zgaDghjalnY0@VGM(LlAqhM?2G29Oj#dBnh)Gz;OE*;(??Zd!oa#HewD)){EFQ1%HB zHx%PJa*UzOH2zq7!JBk)p@06ya2%MO8Aq9Y%w9lr&~O$_B8l5F?Cupx>YNGHGD!1H z#AjbSH&K{o56FV}mQ4_koc-6_7(vp^yMe@mh%1F?CeJ$p^_&Fvo zj}8k5r~KQn3sGY@%ue~q!__mt_Sh=*@~zuL_3PgDx6#3K3die<;{N;NG`BB2g@p8O zikn)6WBl~g_>zzUBjlV2*_^N$LhG`92XWx+7YTBpRIw92yXLyfHKePKpv`< z$wO$B6(+&oP>O^;rS_E*`K3a39RH=54^fisr$iS-@JTsZGE0pC51pm=?t|o2T>_F1 zHO1T70g$aWdvFWxMntsXx1 zJoc?~t!W~m5q3w;lQL&`p8%atlStfb;U-z-OXR30!qGP?_bO`!oY;`Y2s)Sc^mtG9 z#S;4a6^i~WRtNvJl*Cx3_z5*FPFoR3o}91!9G~vL4y+&m?b(O;+($t~-QNZr17BZS zpWeDWs%K0-UoJ?Kb{eh?Vy~9)I9xxjRJyC8;i|rJ2A^6X zi%d^XNBr0MvVmso&&I~VeM`Uq-D2Tz^-5I#H$%9)OPxjRDG$#C2|GjBkaBgYxG(j0 zv$E;-y{JSB8vp&QMcVV>3zI1)7mWMY)R?6<8rg#c;e0>ea2YIGW3`KK`az+eJQBTyzaFG<$%uCW#(LEz~z5|q3&&keAqEgX2&V$OX;?GWo*EH1Bksu0sCr?gZUH^!ZoJU*!;QDj< zY_mJeXZek&;2J)sY>o7_;f!&+$5*b$QaVTp$$UhKxPhM+J6@t@wx<2^x@>Jb0B=|0 zlczyg1|JWqFH+$|@$mEiJ{LCVF0S(jWEJW4k1hV&lg@JzDyRdgi)R!odu_kxY$-u} zuF>gqZA%8PVr?rcaylzDGRp+28*MUhrGJ!;e^faBYyP$Of(Pzr>Zd3Z=m?j)DVpo< z5jpx0Z8jYu)J}t+@8-`{RaK$Fsin08mWsRT+evi+$Ol!b*=DL37%wJ>eNfe)PI0c_<5;#f2eLLHNlw8DWD8;#kLVOXzZJ zX2N_OZER6Uk0samG5fZhJRadz5wUEQkyA}BTMVkxK4r-}hY8WB`JtYp&CVk=PhR-f zm&;FK*QRVKUp6GpT2!B-2^}N3(^b zelvQ=FvpD7a?Nkg{;W@#b0%x*>NpwsU5eNbY}9blO5MU-}}5lUFO0P&km9s~ZN*^NCX zQ?QSpPDHeuk3$EcP^&HTOiieGmzmQ|hm8o4G+F?h{3^8M`uTe-Sw%y0JDJ!eD|+}zv|bUn?qW8XYm zGdY__qUo`d^%Q1TWA%F&sm5(@m7%Mu^4%?!N=hC3D>0@Lr(b@;gpo)6ljYP$Au_e4 zM)hGqf=xq3?l%ILV`~FSs7gyE8fhAN(-Am<1Mu*fz7Z@ zX9LnKUa7ozWR-C(x-{OV2?6AguUB-Dgz-d=g+-++8XuYt3(^1AZ@5Z=yrLVATpm1o zjRXlkntSW@G5#gzF`s<~Rv9Hh>XLffe*Q?m}$IvbN?~)Sj9S z^-3zty(+ok`wmHA+;z9j#<3yaH%^q24NVWWZjEBd3$Kc8Pqu#*j5miXCRSP|Qgf7puv=e3VCz-ntD{CLsq(P>Q%hTpb6Iyb6L{ zva{r3!otGMpEb+76zYC=LSq|pvNzs~F~xHE+s#AkG8}|o44II+d^*zv#5o5JTOfV{s6wTA zI=!UaBzMB4j}$#ut4zBH$u_=vx~A;)9_rSeVd46_#ZX$>%_BNpSpwR#c;qlOCgLsB z{Z2$Y*w?RNUbng7N$*TtqiGtoEcOUn)- zx2-a@CJ5A_f?X7ZAGI3ms7P%w|5D?}-&0p+2H^di=}sU^4CX$$-)xc z=6+AR>~EGZ6EP3J{yw^Isi5T`)qg&^RZ`POsn!hi z2XVPN5c?v6g9hmfp8lZHpfBZ#WMEK>R`-m0cJKELGN|jwc62o>3WrRAE6Ht=$`T1F z@3E}VzKc1TEnf>|sC&DlRIC7N%8>r(x&!%pqvU6Q_qMtt67eV2oDq^2+a#UmKbDu5 z4VcLbYBdSEa(Y5Sv*HhgPtT5_(&-&?OP)S-o0C2Q(wNr@w~(QLlu-lfSQ@-u6fQQf za-X^IHV2P34!-rpmFt+YW7q{EZ4w#aAp`Dl>YG!F$d4JeZaMN8*=ia?fn4LV<)2*$ znd?MzytC7jttFBvQLfoD&4z~szDqiLFAny^*q?MuB1W9ksZ60m1^v5 zV0#P&pVsBP@~rj9{BM1^5#GOoEUHOx|IQ8Yo65`5fyw;1H5n$vXu3HEOP>q)T^&N6 z>rF#ytZdd)*G_eh=B(F)w!WZz_FREKUn;>c<%)hfg#59~jyXiCB65=_JeGDjM=etW z|8D#?NdBFm;Z3DQ!DrDK50NQK2;svB=Ng^)+r|>x>^SpSr@@+{83~b(DU9-ztZV%C z79DXn8q@f1cPA`|pc6ebNYWzj?F)0yn_`Kgw zp~Ef~w|tUaVq7+vQrfevUqdPbR#j5$3^dTc(?E`vJpIZLn`XkfmZOO!&v~%)3_Vms z$7X)lJ&%rDCv#*|!ttulgYw_M-B%7%xAXU)MVA>nR81MmK^n7ZN?b58v2(U7ee&Pcf$S99vM0v}A1u(qI5wGY{}#Rs2xB&$Mnt!g#I z7N+*`Ymqxy@Y&xww$;1wC>q^E-svAV@r;~&tCB+^6}sHXiO99tLpr;LVCn&3{XCpB zL2hhj!O7p4&YIvdIO`pp(h_=lb%hI^6SeJvcl@0yJfvTI2^w2K1nY`i)nsZTwlP1k z?TG$#kTRjK`*UZgg~($wr9@jVVHP!BPsRy0JnFaK+vUcI4g3Y*u zvfK%;5%B*ENT|JE1%BKz%YecL4Ei){doW+9m%d*|Vo3S6;kKCgGua{J#-_&AnA@Pf zwu6Rl>41&NGVEYoDc-8-xPaQ|yQ76Y3$4^K^89g!)uyks1{IyzM+p;N(9|2K53)mD zIr34l#c1`|P@6#QB|ks9k6+P)k7~~#8npe~1qHBZI@ox=;~>z%X5jm@Xto#E_(|N# z3M;(ZMWqw^-hQkNTb}+sJN+ZHfbgNu&9@ZQ7w{7%T-J9==aNe|Rr%SE3N)`lf5@_aVt z)BS{W!n$aFIoaAijY_hXpLG4%*sq|%$uY_AU~g8hi>;CMpb`D+dn}tG$Vv3$(bqeh zbSXU#zM#K}PnV0*yZByl!`Q96w^{L$xQGv?_5#o*;^H92Rmsc!o_+szopQY?eTw=D zlbaLc;GGZya8pQK+DPc+`I^ti3$e#pyu4ri`YR4C*KK46HcnL$E{7xUN7Wd#3lXB5 zykjIP&{3&%AQ_w@K+C{*e##Z!CNjD&@kZl?^yjt*P{uC76mRwB&_B_&V z(%G9Hx}B^dG>^MaDxJ7pqjP~tqKz)L5^~D-jKM(ROO1TZoxb;On4joTT-}%B63r0h z*miQN4}efgaENt(AGJVOmdPE(!Co<{{tWNRjH{lYJsT#8J3r(bWhpmcqaFYW1jiO7a*T7A6n(FSOYb@K2vSuUlmv5x@Fd;6<8g zg0Y%UonfTI*W!9KiiGJ-h+_tnp1XbAe9a<>w- zA^=)F*1?ZYiq5Jwy+=GKXP>k3%N3xX2c)JKB%Z7P>r?VnIupW=nN-naj-{vX&V7D@ zb~gk@R<$Uf>-=9h(RB5A`GJp~IxSTNFjo`;H1L!y;$)vkSX zk<|$MxZ!?3tL8!0C%6Ffc`9iAs4q)K7)+G%lV` zispP0_>t9S{Fwi}3Vvf<`$Eh$>gFQV_v@anDHjEfB zLt`7VmK>2I#Z33&6)_Hb8+)sQ1=XBZi`I+&%L!XQA5$WUk0oq=K8vhj%Pus#d)`w= zRXY~$hI8U(fuR|WW%?319ihRHsY7C|HIj;7Z-5SBQ9Fw%=@w*Long56sZDezy|xR=r;*Ba!OC_C z0xYE%MHOxsr1T}huwQbjji9gm37Q{Y{6_Qp7#Vp@xvzUqy_FxlT~icAAb6`?a=UBkJG4YzZcmiElm}_b8cPhC2=uj00%FO zSl03xQWvMfJ$4OCkSJ%YY7RJJx;^N{C}PT`dOP_u_-NOK-5_n2fiqlP zOuISl?-(trGB0h?yOf@M|5)Ai(#+%aPspL@4HBacqJcfvscwKQEZ3T~ivZ~@-;J1k zNtEd9Y&vUu+xrhIUM<}1&7~iBzp0>-R%l7(X@7|?V}0JM{%QBTK}BjJ%`XG8SN_oX;w+(NK>PJ=(_Se%bE?!hA*n zSZ^Qt2Db-PK-QBg&E*we`N=$HWe!ROVLYi?W1Bx`6|-|Cuk0Vu+Ut*Rih4`RReiEUzio%Mdo7eX;Hp{P0T%Ta7$r7!4+1~G;SRh*ZW8k8E6 zlV_H4?$7C}391f*-QEN{XB8EnTb%ty7&ZGlJcsX-D68vE-{oghAi+<6Tr{jEd>bwI zc5uBWbugVi2oMKAJX!y0k&2;>p6DoNDy~4gsiH#67ah^V#>CAp@uu@`@r;84l|)bP zptJLYv+u^wNcq8H;CanA3N4o@=25wXkgC}Nx}ajoaD}}R=_XZ&OZyB+nM&W#NVZBt z%28s z^*~tC$o3LmJpJZT9z%-EbNal)em-`T$-@VBuLs;bZ2Acg+Dsn%_)t^7sffdXnxdl` z3}q&YQ}Ic8kTeM^l3cLV)yPwcXDnV=qx=`2eLRf~^~-KD*JI29mD+2_2VriUad~LK zDIu0!{?5uzW%+XQ9pPu$Mo-nMTb;cGo}kRRcpqWol(6odgHo-qtzDajZxY;s%c%R$ z>KqWP_jODX#`?R-*|VvhWrT$P>$YUuXX6w4Yt!l{>=jE1r`nh?isAN~W65y4nsg}- z`uhb`37uiDk~S?iVah$GcxjKQSFS*9kkQ9@$ZfQh+Fw_qZfT+xA|IV&#cxB-@Jct( z&%3~MRfypdNn`b3;kEaG78>v*jFZM?nBEwSnGUrqM5BfQ>@EeOke{&peZhfi^R|8p zSD4s8!2>=J3UkAXFq=mRT-k3bh9vXOO{-@rlCHeu5mEP!!`kLn!&fFr_&n=yJZw|| zqRQGU9X=8$ASVOxAR?KNl&MQmnyoThZ@eOv>u`ufJPC_bGEc?dE|(%vbh7uU+;bePy?Y`i9b7I%B-Qr?hi#q!gp-! zBr2KPERQy#ez}o#VYQ}4Xd%d-P3QS~C5#J^!{?E2fNEaR^;FNpBSr7s+%5n$7@CAU zouBgclLyU#3RwJsJ1|g|F|_KB4Xbj^ko6`>*AoS%LOm}ureBVZ_>xfH+A!Ng?nl^@ z=E%RPbrfXhEigk_=s9dx8d|A-0zl_Y{Y3kfai5)r>bXLN#LZ`f!0L>I5H{|pp7k#` z3Pm?yy8sNVP9qC@g`{uixH%q3@%6KOBMd?d*lLW6wnPTe6$J@t>1ZyL2a2Q!c;cHs6+GIGZc-Ss{;o z>_s)s?uhy|{DrFQ-DSsIAXH<*lLAv0 zFOdqa4tk}077N`sE<(u^3HpAHJTUi8!ygFu z>`465m&k^passkr1O-!K!3w)ZbO9yNW_!j>b0!o0(iAWk6?F#MJ<`qZ4^r9i(l9zJ zTF9ug^}dUy#_?c_vv$39Dy}OVtP z{&*WJtIP;bkd{HIltmL!xcif2;?>kppUaS~DFOJ_?ZD5UEIy!#UV-VpdSKS$l-# z>SG~OdrJ{>YxUZ4h5Pj7TqGbvl-%(n_RB=_P2SZ*wNn;9XwPpiZVpC}*L zDa5mxG>{@V4?0bVnFk@Hq+2=N;L7zJZR7FXk0VlT8sD+Lo`Ee6OgwfjFSW_&x1LFv z2Jn*ief?c%@AeDm_S=!E7-PN4U0N8M9i%*PNFXER6K|`rqCx@SlZPwy7(4|9SZB8I z+y4m`Md;+*erG#KK6*%ev>mUaR{kd~dxeV}^TlYR)6c2#JZ^Qr6*uWmn-g_~)A`a- z*Y8tIq1=XX-BwlTJX&+f^B!vjNxnS4@fW9NEYK`I$5rH+Vw}U31~kazm(WkRmD?-_ zJH;JHO_K<=V3*PiX?k*b7d;Zg@m+g7C#>G9-_{$DH6~siZ1mt zl+B@45>@wabdojmKI0sf(dH5p8_?k-XedPFW_pB1YB9x+n^}trk=7e2FF(x@c9+xB z=1qd$z)MA6*?Y*!PDz0jw*De@!oK*xQY5Rt$t;kl+O$<$CnH6Q{m?PNaTNBOWGm^1 z!0tjLHb|DS=n72Gh8F{V3`@V1_}Cfn8J35W+N48WUU2#`UO0n|l!SCAP32(Wr$-JX z;a#)j6grWDt&$4i>XHl>suewY$kN_9OVQ^r`^RqNWuxe_mX?aVnZa_Ja$Os;W_l#2 z&U3}3M8|O1sJJ%h)gft?uXF*>bVRarF&hbdd=_h5S;#tx{fQ!bpzN{0QB;yyg1fCx zUw`Lq{ZM>xIGuAEG5nYlu)x5n*49#IE`mVwDq+c|YjfeWBf_X1i05vXHxWd(g_k%1uFfedd z+c6A{x$Fk?rLSXpj#D$HvbR4f8U<0jIq%U))yum?uWM5-KT%P)jghQa$C}h-j7^b& zA%~QTNJ0&KHF;U>r-|~rD zYG>9o;q>kiaNv5PM^`!~yO03z2h{C*uT}e8BkPk&U1rP5+hZJ9?jo^!TOpy@oT1V9 z^v~$Q2o#R0DtaK&01ikSImBMv7TZb~uj|XVSfvJ9JX$w{FIGVG;%#}R{7sjtg~syL zH~~hPvR*=)i}?P&7=UnZu~9*khHAA8AD?uRSlDwgSEpC1ySWHjKnKte2@}yU9bQI`!FKqqW81WeifKiC_Lsq39 z>L0-b4z{)h=fv&n587gMa(E;Ay1A&WwKv5ynt0>zx%H?bzK4D$=3`hMz#zs4Yjh&rK>DGyI5QPt#~qFG9VdeN zAlX0SI_RTy9UgKBE5MnBFja;hG?!uy?krA&H+2@jnm*u1pI{-!C?W$A-~l`PH&KfV zkul_#HdD-VND@%r8H_1q zqVoW-WR!n*Q^?a29s%0a&Jl@r_r~l)y1Cz6t!KG$9-=G)rT9GgwSt?v84d7)YNrNU z+afMU?B}Dl?!HUS3GJSNr7mI=9Ma7bP;$`2-ECyT?i`K!BbQ%ny@U~3jWQiDND`NB zVkpz7QF^+}=lZu+>H7CIaAcL&nxDi-=(43{s61znA6p`d!FKSr$=*oWwE|$Rp$3sS z`0P<9Z9Hg+^;rrhlgA{4;@3Sdb+NIsYBu9$s`Z1>-XMXAp`bos#i3?6`SNIp7Z zD;PXprSCXhjRSU>NVI>=vSpJF_1okqmv%XW!#tocBSx96$%>=(9hdnmkUg)k6Vb`n z+)Oa~aK_{Equq-vemzsH#{HcE7$%ud(w;9~H8{-0Q70H~!*3|s=|83C8R$suk$n~O zTuW^#r+AwlHm~Qt`1=dowA~W}aChhzH+IjMh}N>xHt-YVu*lw-hSQC@C6Payi29S+ zua6}*P-bDvv8tJF4(*jTz0ys8^sMc-d&}Fw4WgcChazCCtB;P9iH?xb2Wr^gA4zNi z6DMV?)&t8SqW|80;lY;M^)v6q17x4p6D2yF`6N^X`D;ju5l;&2#Jr=ZEZb%t@%R!@ z>448Sfkxu5VxrQN!Fq$H)!#4AWI19O;2r3q84xNXoEu^#3YK^Ofh ztme+XWL##=h-c-d@=Q4cUw<=#0`YfyQv)T>KBosOln@DzY!_$?y$tx)5883WFqiM1 z$Kx+Zi=5k1OPOS&&CSgcn&jj*)jh}fm1$t8^)1joMyrjS;$Ll&G>p6H^Xr!Nb+)Rn zKq8rKco22=S9)wui0Y5yB-%93r9V?z&r~hXlv}FvDCQB?d4QcN6HyMl?uI80*!uw6 z&uZM^`}|b^=!k>PZ?ATrd|(+bA}3$81nlvEqvi+A%) z-dz6{+vk#$iwwqd6f|)5S@hU+rh~*Ax;Ud0g!c9^*Kg^)onJOF>Bnc{PZF3`&wMo)d7kz-caocwC+Uz?+8(p6YEl9*0 zctDs(bM#1`NCV<&Y!3}=yl90KZ{0F&?#@r}oPn@NyZWlv5{%KRj{C(|Gb0wn%Uw< z^c07(t9x;=)!6#G5)x?7l(*^+Ip~!O-(4-@#pf60b!(6`$zHqQ;I6e*cbW?1eF=fY z#DX2K)Ch@eE`SfI*=0#$h_3fz(8NO_vUqxG97`{I44_K1VhEi?o4xA`t~hI$T>=>= zd1G{KHJ(Ceci&x!OKaTE-f34i5$|FiC_5mt0=-?X?6p*bZZMn}DHs3v0xTmFpHR%x zT6`>8Y8*X-4twVYriY!>`pBcRM7e#omDR9k`BJ3a1nVwfXB`ZeJr@pK$E5Dqd3^lJ zd(P19WW@d%QzAPt@a?=Ksd!Xm$5PllJ?)WTn4Xz4AY>L~P*e?1LT@i=qYxE+(lyv} z!1eky3+faQylmC7zGPJB9b{kq>v{Ykg021sfQ$u|wfKYjSZj54`uU`I8k~m{4l=@c z=6-h~k5H`KMSwY!egqp^hp2dE{$7`3jZ2w}2k8okx`s*5K3enYr!?(t-d26>b9$H< z{b-PH^QcVhfx*q2h@LmUV1yL+s2rz@U>ghCws+CvO~<2e2a2clsHa5`HJD5lT=vuk z>eol;`lrxt3-DcL7~?!VI#PKv3oLK7vQg<<-yC)?cBl$!Gu13DoYfkPfEYIRYtoAJ z@MW=+cwwD^nExgJVzXD}c9Rn)XS3x1PEj)}MmU=uTeJHS6Cgq=m~KZ-y{`9>aJ7AH zrje%QOoyzvLGWS`-IP7L&0R_WWO)xGkJn3Biwq3Jb~vA2mG=6jcZ;#zI}9@;%*vKL zY5XG)`{10(TQ1^?fAV4^V5DUPFM9t;HMSYtB0YcbKmchPs|NlJM~V;81DRGdO#yR` z?jt+&%NyabiGlzcN-7;{hk|ry$aOQ6F;HdrJ5RxbdqxC|@EzhoMWiG{bbd4_vfxAq zfQJPwd3nWlCb$P_->g5oAJD61OPZ}e&jtnBd_xUjAMoCxf`VqBHB93=RLs=}qaTy_7+oE&$x29assm8x|vaWOb;D+Pp zer{=DRRf?zFWi4dQY1L7FgNQaad7Tos0yrf;Vf*DO%5d2 zmOMJq>eaE@kdQjWd)PHO;!a!L5h(b1NNCy>ZvJdVipU_jY?SH@M#IMLW)2AoH39$D zMPRA(M+65DxcgQ9|6WW}l-i!?{c}o8-=Vt&<2fJMsjir$k|KC?=Ct$%=Yk4M)5?5< zBbzM8hL>fqIxg@B67}M-2MeqG zKmKYw>BwpG!e$=Hw)Wc-kShQr1>v*7O={Ic-(we`MuQ=K^Qvh)1Kl;KZr~`?431Wn zHaEb-Ly#O)ZPIErK(l-&BFMd;0|_9V11Y?;q``_KQWSW%UE}c1oR_K;k(IYRr=}yw zn9QvmHL6wJsRONmDMJj)e|AzU>9eJPczJ%X)sW@WoFs4g@i0JY7T|7(zT^_};|#6gJ7JF6Oz6_@@5@XpRMC z&**yj^s4je(f<;qTsOSHg`8t94#26J%iPStsn;7zx|O#!v!4Ye-hjkjo|F%4k4aUt z>+UaX-0)qnaM|ayyzY;cxm2xuGNvq>Q|-7c3w z2F|m+u(8M3Ib<7|%`aSoL8sZl@_03^9dD*o6oh8E2(Um64D)Ey#7BPpePlb^s7!l* zyGS{;-ze-i$OMm8(Q4Wjbw5+9r&xc$ie&Mw*M7XCD>D9XU3Mc_oSAMlQ9emO$;XNL zaCQ7A=Bk;oNgrQ{nvQQli}B-^E>3+_Z*Txp4|L9vgxBUWY5`lwu|))+^1J{i+`M;Z zBkqp~x^LHxXOO*CQDb|u?e{ne$aVCl6qLRGG(J+AXy3&;4VcWQXzkE?`M8O37znWP zOWLV2jMZMC8ltwfcHne!plp^IWzKQxw4Pqwx6u}5+Sarxwsqhvj@?@i&aoB@eanp_ zKg}`4FygTX%Q|ydQgmLiEcU^6ehp(s|G;{q1HHNMv#P9cvEZK^ECSy=g`dN&hJ0U1 z;(QL`h4yEc%~Ohm9x)4Gqrkz5S|1M|(itWum7NQSL;hW*HShhK#@?$~fMftQe zo)s0q zCLCf@Iz= zqpgmrNbweqjW9<(?syVDI@l-o{GeGoB1P$1+BE$X<E6!}P+W~$e4nQb9o!$PrJqU{xZkZ4WVAcDwd#U_Tko^@UZ436C#+hG;QB~I~KB&?o z+N|omvQ&v8uLWEVb1YOsZ}3`H*Gs{g_V9}!COES#WNYIJ(xl!%e@*t#N>!HV!PoAv z;)gn)qJv+05ij|zr?5Sx#2vMKYDCfcpQKpR+!_nErcT+9;rm+Y{r6|y=jv1|0+2%@ znVH>uJtyDE0p0}gQs&?n_&2%eVSb`sY$E|Fi8MQ111q7=ooqBmJg!c!dUfG!q2zpY zCq9ZO)Z|%-+v>c?M;qSFooyL2@1_OX47M70xb^_7=o0j@YbQRS$1rXXFKJo=Co=D; zo)H_JHE)jC`O~ zB_*dBxNk%lnT5S~$saE^hg_Zh>q|sJ;~;TA|F=`U_WR;UXFybo_3yirYnj?;pl#ZG z(qaIZVxH9>o^()B``l}V8W0}93iv!+|Lrc4P;s#<`11ft8qb0AWb?S5@CVzHtw8dB z&azWa?=`c}J0=2ig21I279zCNYw*qN?$0pReeVs-EWoPRzqmW6`ahDcI~>dQ{Xh2J zD~=NJz5xjO>}2@gOr1vXYU#_b6Ld*(1A<88YKF<99ya-*LQubR6%I-1mK5 z*Lj|wb+Hi@KUCpw3SWItl@mP{r5)+Sld(^Faqz`0 z<@^7AE3GqtS_7xQ-=vorFZ`>hwf^O_CJa5CgI&9sy4Z)I{c`2L^GqzGTlt2&59t0t zE7YS+X{^N(-0B{7OHy8^wHdA37xz_7&9$no*BmGAj|7A*S$ncHk`3>jb?9`(@GzH7=be8+Fw(;L z;kvdy#P#$8R{J#fBxW`)yBW`xuCU<50-Ts(z96wem8U_qT0dZC;GnnCd(Pn#Tl-8R z4kZ7-JJ8gIM~s^GRxhP&sA|E~d*tkF}SpDXd8wmJ+W@NsgjQrTir%gS$h z%PIh}J$XSoMcBna71)%H7EBS#=AlWNF%_rC=Bby0#m}e&EsF%&C$8q`VBcT2 zDi{3;TIC!l#c0aJDkn$B7Vd!yloZ(<9&P&FG*`cFr78`B(gN0rRG$TJVrO(q`)~p= z_4L&KI6N$TJ_UK^BTr8hx9~d~e|jtU_l1p{#WRI(kD2w~iQ2y1w`!a(uQ{%o+xL}9 zChVj;I;@IE6JPpUtm_5;V_dc7eM*JMk%2WjbNj8_iHX54w|S~kQA~s9>_~u*We6?l zgHYBpCWR-{%-KSdyIX%iHBNrQ6-@1fO{WE& z41kmX-OTXPlY%UQNTb|RpL{BuN$&rB-t|0 zowezifrjqy(Bn)hi8PAC`yRp>4T`5vG9qC(Cwn*=^9W_x1To?_ae6wA7Qrb2bu00> zIi2P@cWfmGNPMwn@k=(hcN&=uh?itZ*-&yn!e^DKu9qZ|kFHu-c0U-{*#ZlhHX0R3=j* zlrcqHKSd@TAD86O%{fF}NdPn-b$0eXbT4rtrSAcA&j=D}qq)k-9oG_%^(lETv_ZO7 zD<7o9a4C@Z>3VOVC7j!0SQwEb3LlO1d^&DJSx(=4{TZPPREiHVE?2K^ejQIsc zbgYA?MUnMGrl%SFZqK$kxsl5H}2nd6*KUg$Z8mxKe}b*Wpk)O0we<+trXv z4M+p3B*OaO^Po@rpe=RFLlC1%{t~n9W8!g(DEEVhjsi%-kDrgcBDK_*0PKV&4R)d2 zt2z=XL5V2%)pZ{6=;Pv`_p9vvIxCTx2(Ua>+~lCe*fMS=<=KkXnI=Go*&L{ch=pNw zU;!teDbHQNQDtT8|0CAaV6uWK+LbnP#BlVR)()x6wj>}!u%F>S%g<%}7|HQ9z&-i6 zlXoR1!l?&;-vVT(H4}1~&*8{~ukxr|eO_+x!KekbXUGI?Cdp}7ued3 z^Fwv7=B!ctFPyIXJ9?&_pV*s8U>YkY4Pd=dw&kH?=PaxEqt14?NvU+W%Jdy=adz_S zIx0$4HshASn_>lpW7CuQ+ljI#-WHxz@+&KYntHmj8&i!0g;8dnnVi;%?7csunQjP% zg75l;4K_?urweI9wyNyawYeAoH1bOce4HbLy$vwilEmbKQ~pH9{)FC_}|Je{^ei% zm`}Um5CITNO=jc;(|cq64ip^hvqI$R-1z0$w={`Kl9lMOt-hwLv3f-MOvxW{WI}O-TXB-gvfj`h zk8_x}S)&-;4mCA?99FS`GfWSs@-Fr<&uYNGm$!Gl|gfR{TE)JDa|k}e?56#%Vk6Q40A2@V5La$ zQnvo12|;=_ADPiS(vW3>{FPV8_qo4f1DLRl0Cln^cts_f*N0gbYSWF0_}+uNh4xdI zS84gLPgQ#%4ygA&i3(WUVXb4q4aa+Myp)oW@t!>D{XY-QoGR5*q|C=jmOx(dSa}fc zh?63jaADF1+l4ms0VaSKRC1$KV-Q&(OO-geSvzEccu9_I&<{zb*RgkGPQxRZvO+() zDm111n2d**(nfa=I#(P$!Tex_sf(`j)di&DsHzEw=5%l0c0Zav0TfCj2X4t##9i`B z%gx!Xn-skQx7W2Qzq%-mXOB=6CEe$I*;O+<0N*;6 zngf4Fh$+lXa@mxoyi3OsrHe-aA3ryG#gugry4mT3!`=m%E?_jO@voI!n}7aR&PORm z(wht$gJP;{U;E*iT{LTTS9%luG;Px_vqaeJNcsKMf6Xg|-eg?C)s>owSv9MMoLpB@ z!B}08{nw1pt)O`-&1}p^Bbokwlm+Ji2G~j7XfZKi^Op|Xm+np5egWi1ZAK!PlGR)E z0b=r}bAgc}!&v6ORV~P?cL=-v4Hj>x2G*L%r4W$-+2^>aE&cW?b}W z_g#dOkLzQ+eg)%5EiY5&s^9j-`oQ#Pi>V=w%pU8^M9H@%wk z1+#yTNfR7|`0^)Uh~TeNi9kmKo=zU)cJ|j@;o2=?qmf$rs7ZR>!`fXrGcB$Z0717{ z0uM73Zq{fNcd^Se?}T?FwjL-BM?c7BBadCNCpIs~=UJtD>hVTYC0ZgCduJunaO#w^C>t@L-6}*F07s*-k)?d3tH1`yuPc5$2}68gCxmtRg4= zBV5b;YFGFJQ&1~j8(FDLKm|oN$&cvBa#%9_8$7;&>DY^P^s+yF!;)C3=OU8YU-~W_ zA!}IC;N5#>7!1@c^k+BvRk5NM>WSZ*wQ4}O>76UH7}^T>ePVI6pzrj3D{-RxE_ze2 zl#-JjMsZ|*)|$4pkfF!VWPI8C48Hp3zmojXJO$CFmTs8nz2@AEGH|G|ZQtJcDcOTt z((OW#*elFDIdLoco85g+>8v3EK^A}Aiz5jeT5{Ez8u?2kWvaEc@zMH+Oy-G#)paC3 z(kQ1snZ7#2b+8Bnu_Z{HupOg~zEgi(goRL}CvZ6K;bX9nVaJ=_%Fs@%RgV)9QZRop z_$WTh^ZxB+9ArW#zLTw9PGD}2f4OvL0jBu~uKrZ_g@X{nSDLZG>{C={MW48`*>gp; z`VwlyF)cwxNM@(Y?YXkTfI>&gdR_nK(kms3``J&jm10!yRla+-Iy@qlPL5Q6h-XWc z-A_>&&MJ#NQ{hI6WV4ff8CdS5kX&6=QT+mGepK+Fyg|(4AoE8~`xem6BW3V7D{c$?o|}M`cg_ z_e;s#%dXhwL)b)iBjR~VvSZSA1x`VpFo9DlTi{fH!QUxm^2s^0vYGb^LoMT`4tiKs zY-qL|A2l%*OpEKQ2h|^;LY^8L>puc6)1!$eq^g=~sNb=?RpMla;3wWMW@8ht65~Fe z&wc}-0#AAQ$I{PfX*+!-Rtd9C9RNwAP|3}OG$(tGnqjxnovK)usglap%oTCa`@m$) z)o8Z!#mZ|S;dr>-zTs)mmhAh8H%_vGvv>ld6!Mh$?^F##ga>}gn<$}UxXB$egL;*PRIgRc;YTyUKHu)xC^n z0<4C+sa@1b{O5t88Z2`rTrglZ>G(j54Y-3Wq2Ge86Q(rqB$WeyQSvgCMdhrLR;lB> zoKc@G6Pjfh*I`t5ba1LS;@1p=<^muWsH6?LbegJ_3j(8w?1K$f-jgH%r@-ktH&aAu z#?1FAgfoXNt$Tl!HBsO5pMe^r9NlW&c4BvV{1;KM-TMB&yBwHFcd4nqY^(}fgm6ms zV!o%q(|Wc3%NMYwY@op{vEf6drgwpgO1ph1PgwRGp&fBaCxFomq&X;r)%s5F7Eb67 zJp*V9=9Don;*4?CCO)%ZqV zmxYfwF&0tv_&xHbf29KK^?{9P7?$sGTT)@^F@Zj zc8b!6QjskCkbE?VxpByY0p_X~d72=5dGojX2Lq^Do^qvZTVnRNJ~q}PFqDs*Uwgnp zj>3B3qZZImt3~;RFqyjtqI9Rf9qk`j@f2)5FPA}R_IyYe5&}FhITLJgiVwN+-|Yy# zG}BtKvS8S(=AS1{6ME&_P^hVre5+P817IQ4@V8;Jrp)Rz?Z=p0t!KfLQ>6UMbD4|8 zoKTk7Qx}(y@-=*Ady~#gT_VgH#Xdh0h)sEt_@Tc}%V&cKk@T8angMHINeqUG z8ICT-evao*eyLJDTJ?i&Ir-VRcG9}xE0U_HEReOZC!$tFriwu0maV1LKQ>zxR(?t8 zA^$7<0Y3AP(@h715qpp05wsgCaYTfXpi-?w6W4gc?VE;1`bf5v8n_m==@v!`jekl; z-$lk{L26Ar96wK{@Kooex&>>FzDH0Su=Ie2gf@FwfG8}r(~s}@dotPGh=H{)kfo;c z*iUG*No@kU*(C{L7*t>g;dctd$*Wrm&COMrye*CD%Iutm66B&b#9w@WEBM<=cbm=6 zhY_WQnayWw5s(^u7I;h=2o4uOIwjN7#~d~8c5(r*5B_}j@CgrLA=S&djqws1QQD4r zK-F1%x%DhU(YlLpCa9BPu;t=&4#yk4o>0rq|0uUe`?X-891Jt^!qF&3$9uQC|GX)W zeQ?O4JY4(_`Y6clLBA4SoPB(L3S=;p<&J!Mu@Y0Rf8)S%gBmV84Cp#up46!k2kWBx znyuzFhSTzI&yq5Ndr;k9rqg^?bLqewLHyW{QRJHl=tBk&j`i=ZL_Hng6aRwzv$LvSuuA`r;5$Op8E6M%cVk?g#k@RUm>a3S*6#3lp-uK#LM3a@*1jZKzM7!* zt<(Fnacq0(oJov{Zx5|jC(dPOexSbyM;o=|O#1-|4bl7{QgL?jt?liEt@c6On;76< z;Ot7>tG8#-1_@gRzIcL~vX#(nDyFrQC+8z6qqGkxD0sxhgu8kCRC@ac{-Pv2FjpOX zme9hwGZ+FskxcNBQJ30vV6xf-0g($0Uabk5GD!&MT4oFI)<<4mfQn(lPzLDl_u^%T zQ*!-HEnW;JY141|Wh(hKhS^C{9Ig&Y&n0AMf1)9r1&|;8jK-*CNj?7{1~y+jCh+B5 zS%Mk`fmo-yCojV+NYSlb!iO(gk-(6mx*VzaD<% zqH}=ADQaeW7u|;%i2=-w#>#}HY_AA{ogWri3{f+MZ+s(3{HOHv9Hy|7nXe2#8Qw4xaK}!rzJoaAg zeGP`b4#o%MW^*} zF=dk30D`f&u>RPX`0;xj6EM@L3KoC2AUXdBqhg-sLlLRp`dq;&8}NJOXz;My4br#V z1#uk8on2|%5~*TKw?@z5WTAci%j9I?HB>`b)6Y2F!59P! z78MK<^;u#abM1#SEF_(^a?klAAJ^E@LimW9q9&Ip zFX=6zYqhbGXx6kfc4-=+x=C9Mpq;lFaI;}u2AG(@VN|lo-A<($O#3zv)vHJ+eMw3e zb2~Wql+U;i%JIGbcA%s_{Y)jMklV!QYWN9pPlM7~ZF1|89$tW3EDH6; zr{+qyJa#ebCe8U`VwhG>te!u_`AfT9e(USN)c#E;CwS|x0LJUu1~UbDJT8EMq$CkK z_UUAxYJ>2~LVO9E*9vZW2kU#BF$+w<*Vmji6`&L1V9N&m1h03Obd{*#G3T%^T?IRopO^a* zSxXl?_>m^m-!Qsc!6y}XT1BpD=+Zp54In6^q${G;#01n#(H4UUP<}nhSF68;hwbL= zau-Y|FZRqxYz6-NEc6^@Enb_MJaA+9QBMH%zi7bKzTN>Fczf8(fM^K_%Q&m#{pTyS z|BakeHAL9`WODxTsQgE5OPdI4%ue_0WNbU-I%4D;76KIrvxwMV@G~UBK}TsE9E97un}#Eqq&O7Cz4wSa9YFn&MTDxF5Eh)vYtEV;bjhI zPHpj{H6jBcOnuU-nl@AG-)yBzK-Tg0jsY?AFN1x|Ar7eYpHvnRXRI9 zXP87MtIJU?tP!Vz_-D2hs_82tyiOiTF$ zH2b~v4n7;wCrcG3&1%xkX@T+2_v~E2TqVS3+8ztyoB~B#umA#g)U?~Iu5fPh*_UE5 z;GZXteI8}zinhQB08*gq>w`7!3Mn+8CxjE;pfn(N1c+l)c$Rp}a}xCr_ab0|K_q2bdj;;O7tI)G7&~q@_jB ztO?)Hjw4IHdh}^BWumnbHAt;NzaasGSo34nsv}oXjOZ<6QeK)E@tFSYy%T z)B5s8)FrD}fx$A)ccbVdH*!N4jBvuG$B5TOxrY2qkL{c4>Yy#YJQRdBmN}XQav!+l zle{L`$B}8}4-W6C(ji!>Ry&3>Y$Fw%tr0c9Da_YCsN{!yJaDr!mmrQ(#*8=DkPu_Q zHFtA*oZt2MF5hw`ST6r8R%4)a9Xbu<_ed4V0Ny59^rV4-((SxIsU$TRz+;pwO@N65 zq=WmgNyR;m?URab!l6X^rsP2wc%c=nN1o1&OM_uz0I_vkUpV$oc}W`L)!>l%Ku=df z%WTJbIKhPdfVJ8o1j)u+0fGINX9afrZ{%qjV9V%eJnr~cwC z-j#>{jt5daLvL(V6WETp$>H}ylbU;4)olH!qwG1;cBV}WM&%RrWtXe~>H}1Q#oM%u zX^0OtxIJ`H|wtc3gCAgMC;4KpP!lMQJ)Q?w@g z3LHWp+CweTjA)RqQEUwV0`@EL_rl9+P#rx?L&9nY%n|U1Y!=*tt{3H&K9=ZD+W#}0 zu34f|8bi2pXyjr^TL1)?=j$ zS^>S*jbXcnGleaGj^iNNEVS=fkk$eYnyXQT`&lN2nl~VLyKCl;<71jf+}K~h+666N zu1K0T+r{^A+(fS-pY6$9d}uFkgUHh~^J(mrD-#lQU-gdE1Dkc@eK2@jzgWgphr=#n zul<(On>Vq7=#`;qp?09lTTas|bk(uKLCf<702O9{0@aB4sJ+BD?m(b$VQY-2jG(uk zQ``@dHS~{^JKmbP0df%os!+25rR+Y$3yQSJ9F}#53?y(?508V8AJGx0q?|j5^5Y-y zVmW_hV?tynhWC$;3@?wz*?LB1|1{jvD5ri)C;=`*cfNR;AMZXriF}KR8-MN4>8JsB zXnYx5X1UrBYYSG-koH)keX(oAl~P^T(0jBm`N@F>RZRz-2IkSuEXg3Q3LAt*W$s__ z&;8-$Cu@dO7x;)~MWk#TcqTeaW~dOsIUBW`1^M<&H_j}Rw`p} z^+8CLHrf3RFI55TH*dnbUA#WndUnFU1*GA)%6)N3doHmM(a<>2o8l$}Ob{Gs?sAv0 z`NBD0g{qXK4&OZd6wT9^h)9SmLu6lSJ9|O%kO)DA@Jx$HnXpT`-^hv5B0hD;Qj%x( zk!A$4uS=x`lTl)O2nbPE2JZoO1eb`IGu7^>3R(%T{QqoYa43FJrQzeei)5{SR(?8X z1)NT*f#Cz3uAbz;H@bDF5KaOTW2(mRibq(QATY35gzz@_oPn5awm?Ef6Um?CkODXEgVE0tw`gf9rX1)B+^tE!fXciP;&@LIs z07WsMGzh@53Mpf&p3kBJAI0y{%Dp;EB6)j5iG|#vq5w?Mr=4os2 zB<)|$)}Gb|ofkr&hNdd~ZPNCek0KeZ&Obww4MyS3f_|Hu+jcH}_UxgRSyEBg2P4;I z-}iGzCdk{zwko&MFN1g(djCP#H9V-c!N&l`I6IW%?XbXF>Pee^UHa8FZCXL8d7fGz zUI)qa%RbQ5!sL}S<^URVuwT0l2>h<|rfV%pc$ET9ytG&Ob-ZxG zegY9?3ZJpLQsA+tceGjAVaYbxKlvbj?~a@~_58Ha=5cJipaj}$+^|BbuI}i7;T+E=vCKsbw|fX5rFnV#&P}&Ly=$ebo8L{sle>V4awI3PY2`S zzXErnsmKil5mE&*XJwXO12lX^j^vn(nGdyx%L~IY4=OcymO-!uyrX??er#tY^$~8= zDGBK6%gQRbo18#SgK}#^a(ImQEg54fFXPH*{2(NSAWp8{jdkt5C?juS${OwHA3UA( z|JMKdxH?&)1H-L0iw?s^Lw!Su@mp}J?29d4iNbVzuy5jB?5;vre#J#&v1MYXGXx0; zU|)qosz31Td(VRxk?k5>#SC4)MeAcaS*DoA1Sy&bA?Ho@$~Nd)3lUh6*__Nx1-~BK zet6keD$lsHn_a3=zWc6b-w zj*GD z+mB?@@3q;33_`@)JO~ais+pqLYlM&dj0gMfNUlC#u0GeMOcB)`BqbU1#xU>Ql}Xi< z)J}e>zB|35A+g&1qmPsaFe_r;bZ#7Pk+(LC9J%nI3k*<-e^zJt_DQXh5%iEK92vxc ziq{$5@8>dE#X;)%-1KERms*OlW7+5rLv?8{TvLfBS3vdI=<4ar_t(#DI@`&0Y=n+% zD?)+s<11vTA(*aQK?z4dNsJmw9cv-e&7kyt@cfSMx&>%V0ADmtNsOq;e8ZRZ@Y+-M z@;YHGuHI%IfuuiP@a0XEi)R(hj*S~ysCh^#twyRy_(bh@gCa@Ge?6n^@o0c%bWK#Y zhNGfIN+Pp7n#u66A=jcf@|Nu5hm7ZKFc3nAlh{#*&C|N55dO9J!~$|SvcU*h>|?xk zOZH3b^`BeUW147Da*urlAy@%W8Z8#IIL9&3t(39a>Oe7u=Ueh|_nu-5h7@rlS>>SY zf{==f-K0Vr)lFU=i?v6Akh7#jsWfJ|sP&v6M58|P?QebR@!7KTz{5q&&yHdzi$u!8 zWd!oru8!uinEO=sq?shpz9P^P1~`5B=juId>>i2TOw2r0G$l^c0I5?Zj7^vSJVxN| z5ba4-XycUoDUghgvVwwXL&>+JS$b`rKP7whzEik9^1&X%j<3>SC%6&eP81$W&Bu=A zGe&4Y)cq<#a=B$V9kf6_;d zL{&NNfUWW?M2R~MT%q!N%KdxzR|=l@W!O&0%CGPMLWk=qY9De@**H`sZM2}Y!UqYZ zvQlmlE;{sFpz$4ZQf(fez)1eK#=pOwetNNy*|oo#XU_zKGu(h+-!t8&|CBO+u#@`e z5UdN))#FUSKKB9ksWpKlFiGens>E?_S>XCPy^qI6 z+`;9}ug(34(>NUMk~Hcd-B>QFNM;(>H)^9G+i(nui8)%gv!j0!jYfAcO9Lu_1UsGL zu3Ul+opSkm#T6!;BjI~sU)$_a{A69An9G98wS6Dtjbr8cc4!5*Q#=@_FA<)48F~4L z^O?*3=)>%v?@}*E>c=ecI2aQGn@dvpTyq=t)7+TWle0OoWHkM zPxJ&r0JZa9SxY%MPvX{b-g`Au8-{Xnc0M%tN^$T%l!EmiQDh zQWON8_(_3J7o{pIu7Tc1QZzg$aFY#oc>MUVmbwM>EbqpB450zg+rf{_DRQ8wLJG1P zieOEkDscy=qca{~KcVae^4SycH^b)TY(RqVC*k#W68E6bUw|20`8mmCW=h{um#w@C ze!Bt}^3+G+ovwZl@l12YBYtRPDzp87z4;dZJ=MIg{iceK!a~{tb;5y$~sI$U|$8aA%hjV(unE6mAOp|xPEGG+SVN=?!kpS39jbOA4l0Y@Mpb-$~`*Na=B+ zu6Hsnk!QV2ww_Wzgzk4MP#x%Tdb^#sqHE-Dih|O{#?+k%92~5ahZkfM z-vz&y6y%8$9vJ8MJqxt%*<~PQ9xGtew?Xt_WAh+v>H`Xxaa;B} zadav=Y%XlQB0lmpV|%uZZ1;ejfkl&^terDD`kh_aem!G!8AM(4fy>o;Z25A%l;OgQk?oGt2{Q4O|GS40u;=>wpBD0TNKCt~y;!lif`L^v_R%Q^jkiz{N z2N;^aZa7t`x4wFEi(@_vWUFZeu5t_bg?-BJ^n!3SXdX}XAfPpDA*R36g}4)s?a=N4 zxiCjx&=cIFIH~{eqn}HHG<_9Jb%kA+&clO{k6aX;lp2<#)4K5Q9Ly9_yG#cZ;C^qp z{un`ukLgI(oFO1|o}r8JdxdA2W_5{onb|I7|J$`(+G)K34gtwqwotd1qvqwj))1n2KKxkDykyf{I6~`EC_9VCQiYw=b zKc>lxzUNQs)($qQygBbON1x%H$tS)W?_d7@nROfl z0$hB(LIFtjn^R{miN*-O-+|3LHH3P0LWIwldZpsiZl2rwh5B3;wvL2w9=>s}H*AiP z9d-LH;jxAfE#hnhX-@YAT9NaxVDxTm*{h6&F8HT`uINqm}r!9Fc%eu{ylu{JCt@M1+EPFXds zV-ZYeh^hoe)ctFo0XG3fMwpAAVk*TTijwgGB3`(`*gI|NPln2rL)i=kB!zOjX-3sh z5d~@fjEwy!9lzHkzLCbvEiBAMgEnBHY5etMC>)%qJm&=SF7?HsShGo&qQFMLwB8gK zzwgfCrgn4kXV?Je>#p^8lH9-OGFi^*5c!b9<9<{>>%ZXqkwlO(t8M~0u)8V+Fzh1Y zvL!>bg^{Fb)treAW}cIspRZxNre>3CjrYoN=lAG8ugR%yk%}8$w?X;uVjD!EF+b5GkGftZ>AvxAYES!ApwGq3NW2od=kt*2 z02Dx|^oFrbJHt6=n7o?ypiRU|>EKK_+TP`@=uFi~Q2 z2v&3Y#;PksYi*ViuDX%gzbA4?P-7k*>FxDmsk(X3X7}CqkTLx<#nHV!iifdFr{5Ai z*(!=W?+YnB3EdD^j!-k(EDl5UlA|LuOaR|IktaGURpE+xJREqyUjbq z^;Rf{R7d=673YB35gTFMf(8}ZUX$USULY};4_^A1s$BEE6gNz}H+O}mq4PFJpuNRd z@e;_aTDt2L$pd`W_q9Y1VMjn5Z4eD|qNp~LDR^;Phg@uQn66j>y;)KcWe*iGOVG(k zwdN*8duRPETf3NP>G-`nqQrB=Z_07p6f8z?CqYHio)eAIO6FZ^H-B?kvG?WF^1>KD zolLg8%jMEQ70^UfN8l}eGM!1kfA(%kgbwx)=&p38;U>Zv-UBRU&#PUONjv;!IAVhp z@%84pNAu3=Yej39cV5D2W~RN%TU=hLoOnIUTQ>RVOH;4l(jc%e0f%zu(00`<+iAph zyT7bXc1}qCvg~F0>~3z{#_OJ@2Hbz9cVmIvA&-w-lR{S-eSE*d&Iq(zD@^9a;AjFOi{yvb}p~snzpe-6hNC zSeP+Tc~PvlQ`#Bp63a5nIa+InR{uP&H*#=zeLUM3aPNGtWn#B#%3l|8`d?|~7Q1yh z7IZNj=ND9qM71N%Ud9Frl}`m5RWeddOm(UG+yja=i=$sw2QR+08x#=Vm39ZZvJ{~{=)p zj{LpfxyI&AcHZD-714nudeT`ky(foZ^WjK5NJpC=$>6Ggty|+1?GN~qf=D&x^k2XoJUy9dg*^W@@Aa5*vsxy1B@lfs zN?(-SR_oyB!{{ybH`)8LE;V}FKg5+O8y^pD=>`F7(R-5Da8PXi^HmmJ6Bm7jddr^( zm-I4h9V9YrrXIILe+}j~HU@21bMzT&Y$d|ZGF~}SOt|li1Hy?&ayWaKQio<#T@No& z!)`^z!GFH3Dh;>4VCskI9Lt`os1&rG1mnCrbWyUh3=5w3H7oP_9hjc>@P&jMSqJE# z4EJ!}jV}@Z(zAZ=&OpB|8wfAE9qrVJWA43~L9t4!F=QG*1?u0;35e3=zzlfYnmC|b zDb__v{NI1eaJP%v(*~BlW_p2FC<7;fOUp<9%0h)yvo6f5U!ON#q#>l&srolEkLJ7n zf@R+!Ms%z7w~5ue@+hnXlD91*pv_xVcBel}gs9jhwV3;e%W^eU`j9uVOlMe#WcPQQ z7U6WZA#jkX$y6#7j+qOf7pBQcH|3LQp)QJid48_l&1xGN?fhhb6h#ozEc$CnJh>ZW zS>A?j&#u9fazHX79RYz?T$XUj!a$jFJ9h{IX0VeV-dUP>O_>6v-hb+Ms@EfuBodrP@p^lWI#h7N5= z`EL*1-91Wqa}#gSBJA$&SXZd?*ktc?MR{xhgknVd(RP`pXAVzEn^5cOR9J1Pcq0qy z@|~X>g;A#Wb|Lu!6>UnMK6z08clh@~nYYYnX^>=k(ox{n30x|J&)XO!g(UeVTxT(} ze`6d*R2TG95so>yUtZ|-Yn?|5s1}o4=eGcQvu1EZ0LP6lAAdHZa_Yjl?>nrqiH2^B zWcF~=+~wK{Q)X1uAjg)+7pSFrBoqwHVUUy?It+;=QrC=6ecWUhSo#Ve-zu1v)AFX-xz0bJTy#6WWPPluS_ z@LT6r-}V8g8tP6C6y)*i+oPY8@7VC(E*c|i!W@L1k7L^x?lqie)^Eg9E5Dilj_5J%076L(5`$$1fTQTQ6ro-=y zzLT(S=Y~KSJUO71kNc@#8_`C-Nku!`0o}+xjNB^CuTBjDEH?iP#tr|qP3t%aMguEc z(?`}jJvS6{|Sq} zLG5H^3?Fjh84Sdu#hi(#_$V7}KLzJ*!Vjc7N2%y|d1)NS-t)kI9^pKz zcO04ZBjq?om%=|mSyJ|}|FU=BF3Q$6s>}RUO%c=8;u$7}%hcV;_wnoYtt*C3rLo^` zwNDe=9pSFO`rWXRl9DmEjl(+UqgjyjxYI@j5j%&0u%j`zvHRwB3(?VrYqxd<`B+c` z`<}6PsOUZt3f&m~&`Ce-jHi`nn?F`ku@Twrt|pjZ3R}0n-=R_B5nlxG%O5qk(oA`e z&zFS9w#j7VAf|KXPg%bRL>Qui+ZH62aYP^O6;>A$A~gP>UC$T)ryLR%{b4$*qry59 zdqQ(J`-SWSpT9jUZGnfZfUN)r4)IgpU*Rpid_D{}@$x z(}fDHoL(JwE_sQMy4f6M|A&=a?q`~NAeLY_*dipfXW&U_er^Me=N3Bk%To?I%r)lP-iLFf~!^}_i<%iC7h?cbWFt^5r*XgvFD(sb zbPBrz>+62|5qa{e`a<1!Ia@F%_kXN5a1xD(}8iQHg8{y@9#kw!-m* zD-2C69Zx)&yv!6j-aW*=AX2@Lv48CW7vsaPOks@!ONJb~F4%qoSBer;v`lQ7Ofn?L zr1ue0XERnR#4UzC>)-DT{$?!~e$Ne;QGDwud}dJ)xme^JOkq^f)Nb%eJudCup9NKZ z;-yab*VG-mQ&Ynz>^ir@rJk~9io376Avi2<*NYQ9eTVOxr$F{m7MX#az8kS_sC0{W z(QLy&XLR1{wxnQ2@ywe0qgT&z`95Y`)ykeM!d2-5e>sx}O1A5}_r7;8DCZPr@urX5 z(`kF%%Yy7XhVxFy8$2un+6cP7Cz&niORWfo*IAf(oL|;nG!=;3DRKP3?PJOn^^MiK zfyNw2Lv`-xbf5NJU?6`g6d%JhE|6X3@-f2g| z31f6y$U>LVz|hUzv8;uA=eTUItQsTl{4M11srUY+c!%)chvB6%t<^j?ts|KqSrQT5 z7Yya$GX7yE_P*N?q@6|A5q1udifL49RROcN8FW@mJ}ez{@wYpMnkd zrnwH%KR+x#>GUhKiK!O`m7C3n#r4}iuTKvq3bPvgeklnCKYotTA)$DKe$NZB=g)yy zI=vm|Pu0;L+@&TbPM5u9X zg1Qx;IK>Qsa}UqN`b;V+b1F&r?cU~BKUFi=qLrPL#|)Ky&ar>{evYL7<*$fqhK?s) z7M_T=IoJ?tQlcrg{g~+rA@Eae8HGNh@nyJnmg+5cOVRB z5bqF|M{X&qC23?)h}PCdB8nMdY>i3Z_FJ|tII)b@nL+UL+wqq$Txa-ki*(R=?H}`T z6L$V#T_9Z7u%^jAFOqhq0u^XRwVcqvyusM2L`GQIFh3!~bT%tdDV zoo6@Bo3EV>#Wqv9d?BJfL$e!h`}=8rXzI*Lz9LtK#3e~e}M1ot{g5i3sOrTnUSHj{|)f_#}7f5v+&_LE`y2FEGM zC0n?Xk#2~GvCThwrvhhBTaywx!UYi~%h#h3a8E$z!PONvg7Hy{y83^Wwz}0B|c0W1pc(q2VM(S7~si z2tbq=iqrRRW-Qw+G-CGQ#+I1sONhvdx|sE)&J_?EP#$8S!IUFAxkjvcz?;$c#OlCSMwEO@Re7W?pr>eZC0Ns8S6lc?I7 zJ^tdM8CE4Pd@PerMfKohAp{AUEL6-M{V?_t9r7aXrPOfY!QmOQf)5$LZc1{@hfeQ+ z@p~(w`6!)GhA$3b_w^B0}fYi`*(VE?FLI2Jxz2BnU*`Eq{KZC5}v=vo(qt^CZM4 zHXC?Upgh5iGx&-|SWonQs_y4T9d-!yQ28dTO#nESXrh4XtK^1j?`&y<_Dh3 zk)O3Lw12VTf3J-XVPQ%O0|zm({m-23(Pa+sI(BpcAiTcr9=N-Rw{#0}B4 zH`?YFfdhbaKnU<@q3XVFL=fNUZa3hhL=X_G9`lgLsvt^;KN)viEa3bII4Mv;aM>*- z=>?*71odK6L26q`5`<0wG?@rjXLtv26nBTi0Vh6aQ$qTTPu#)XUf z#wO0}>2+UUFrI^qVzT#JQ8myvPw6f_4^+CjU-d2Yw7ax&7j2&g!RUD;#|zB|xzPSa zX#<*A=vy)Fc7`{8-nZc6LEf)_^(Wek6D4u9rIgqU|CC~N%ZaPI)WNT5c%87O{>F+W zqahN!Cvfh6^2I3RAZj_^dWg7eM;I~$n4`RGzy|BjKd; zlMU(G|E9E`$Mdig3?XAjVN+zsxm*Yp%2PCXFq-aq!*ty|{)oN?iH zByp1;7!hijB!uf4RCYUFbad%H!l-QZqG7FHLVI{?fgM*Ke#?@Wj>g+%!|9Mr z2Wdx^pZ)KZ9U4OAPl?4rclk>4@c!@he_11{-Db}P$KkSj1a_im5<2m%WWrE&Vq@v? zj$$o7#C?LLxFro~^uk|%{lIsveSEZlPs;2x{j(SINfqu=BjU1hB^Ys2=hfruAb0ZA zwBq@Hji!%7QC@)#{+Z7otmDm&Wg96l z5NeSX2cNoecR%CIeAc=UPQSYTzW|&BWBU`YzkVNpKL+sdh78CCgXcJKaxoDXq-{u& zo}2tn-Uuh3Lqi0VWGEyekdzfC|C5!yQmJfYLXC~aivT`(JQR9u%ZOeOfQ1EV#m{B| zn>G+TG`w{TfWYsUm_b(;aNm7WhLo*T4Njl#2k^T99^a6G`2n^H-vRI+kB347%8|{U zXStlTx1UZ+qP|192yaUU0Muc?ipQnT!Glt+v@P5i~ZLcsTsJbYu&x`g3!)^m~WxA$I9^!>a?=WySV-rR~Fx4oNCVB*@d!PrfPtKXK;F zen^bqN!%v26n}u45CA*{;P;P5A{Vw~5y@nLv9VjL{T6#w>S5E$gV(*C6^%+6QsQwb zjq;8ik|A-kRfDrdJHKDs(oo#5NRfDrHjX;B@Vr!NDHM`?4Ep+jwl;}T02`|s zoI95Q@L>QS#ck$L^nHX^0DK9+-ye^~mN%5>1p!!I24-g^@xEA;qV+bBel}#weyjFu zH^jru=aZO0C?sW5X={@{$)q|Jxzo(xjW>2eVgx@0VEa~#=l2nAKoax6bUYp(-VmZM zmn9-!S&=@3*b4>6EcaHhp@#accG}o*SJyy5Vhlc?wB^{)AQ=~%nj}M`lXikP-*iE$ zUGPyzM#JdlkLcSHYmkh3Uji_2JRWz(Yflg)@qKkw66c91UoPMJyM@>c+T3ympmbz2 zecoLzNlNf|B&HAu0L{%(;gZHi>6l$+aQ?g>z)?ud;Bnjr!AEZ~Q~*2!;L8Ax9Zw`~ z`N-C5q)VqIBi-7X^u1iZMeMf;9Z22XM7!MW!T?mA+NuB;k4It#JTLw4@ko+GJT6HN z@wlW~ghCP%G0O}tTxbUHUI35DpVqB;z}p_v0G^XS-#ng7&MISRG}fXhiRalYu)Z#R zGMQThUa3f*VsT4Ecr(~CRs42j07~a>w@5gzSNd?A^p`M+U{LZ^XlRgjDTs6dfHi@i zxOlM-5+itr{JAZIkv0K~0G^jW&mC`Uyi&Cii=srt*{t+gTayU9P^czw^8Z^1TTOqr zH3Lv}^mbzgEV$j0`oQzIJ{%_<=eS#pBM^|7Mlgsc@_8441Gps}JdC%m?O+X(FFsl2 z&mUi16_L+N1YIgg1S|;Bhx|{7`!*BR>&5Lvb8i_2pz8Enff<+u$4SzH%qX6?^G*&@ zQqu58&q-=^aDM&Nd(v$rGzsCtOE6GPC! z6Gx6jAu$8(XBU9Yttfd5IYP@sGzb0|~VEpPGf0cik zxEp^bbAn9SFMgf)%?aF5D~t1suU70IIc0~XG~5yyTr2S-;*odo30?oGl!#y2v27^p Q^;UV!@crI7_Kg?#1(Pq~F8}}l diff --git a/indra/newview/res/ll_icon.BMP b/indra/newview/res/ll_icon.BMP deleted file mode 100644 index 3a9964cd9544c31d1b824fc29826f5ae2da35555..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262198 zcmeFa1$dQL*7iSI_hLbUCqRIN1R}Uo9EwwDiw{4+dnI4=Lcya&pApu7jld!W1r%6p)^2g-Y(ya&pApu7jld!W1r%6p)^ z2g-Y(ya&pApu7jld*HX&1MS4%hpp(O&P` zwd-s0J;daDnZ9G+zI}x+{s%AoJNmu+Zimx3(mpHtKjJkaQGJ3eWyi> z7Vq2I+E%u+voo`?v8iEgZCwkii&->n+O%PlCQTY)^3MiqhBY^uedETB9XW1E+X`#V zaT{8(wpLbF?XdRG8|ORxwLSm59P@lT`5a6>uO;Tlb=YIgF*~kH{x#-W8#Qaz%#v%b zkJaY-&6_uGUXAZlh3{4o?s!kWpYWJ)ns9=A|G&ctQpr;Oue=9-(>)-V|7*O1fmgx9 z8{p$D@bWJBsRYK%8Z~NU-k?E)dX|=!4J<4ytn1gWZ&$Bgy_R+A)M<}(tzElz&sw!= z^}`0%tXXpyHWC|!jWstn|D5&<+KHGe=7xD-p4b%3t456)Q?cpK*-VcA|7?c5miJDR z_lxm2JTOq8q;5)Ty*sx&>zMl=>vmsnk zAFim0RTGXAu7f9p6GSt-X5xeA^g%fn{H9Ux|JKC|=0Ex56RqjRui@`r!3Vw$&fgZi zGyW@sg=%1;4*0MXtb>^r;HMQh>I9~G2+qOVFzgdBI3Al+y?XV@m{+xG)n;Q0u%%V2 zR$Yay#WtFmnQg|t#*Ch(m`8@XWs-?ax{xzlr;U`>BCKu^?x530il zRfG?o*9X#vJ&Bt$E;VDVSS_Z33s;v2O*tl`B_1T)A?kla(q}@}~8r^{-T^Vo;^Z z6+ITh{fWtc+SP^R*wH^s~A_Q5}%AJx!!$T`wqUxCTumlu!!$D3r_IhJBw}*P8dc%^rJtz!3k|J z2YAt%{<0K(K;Kn;jtgFs+(9`H{1%b%fA2M$^t}=DFPSjU_JvTZ+6AHh22#f2fM4y99vwob+j+Bb#f@N zZRv2`u9c%U+g1+O|0&}=@;+^Rj(nbcHh<%4V_WNDE1M>T{QZ23MwXYjo*eVq<{4G1 zS52;BRwa&W4d*)uH=M(c^WDBTaRT3c1${7&zL-j%Ormeb82O+tebyN+Xesf6aDn7M znm2DQc}>YHmh-^>y>R-?e6#0$z36*#UT=!F$H$v7c5C6+8VTkZ+wB?Sy%_7m81rMn zx-))#IyhJi9@c{OuQBv_r6XYeEI!o_KN?cCTGi+pwQ3~Ru2(zVqM?PxxRp)QLR$yB zD~|13m2~LQ5hFHr<+Y|ZLV21Zdg>Oe(g)O>X_$Lt5G$T?-z$w2&eVO&hY&Y(GTC! zADif#mGseE`pT0&lbB%yeJFW^j`XPmJkStMtpyKM{yRKSo*($lAm_Jzp(fnphhCCA zp6GjWT9p{1=8V?{VBW^)=X)A4Kc4aJ$vB_KcwY_Hx8e(l#VZ{L3qEF5&4R0&SC6h; zuU4`}Ba7@tO|9~pHn%Bk(c1B9o30&ibm`ygPM_fe?hP3?@;+F5@Yz(CpWGKte>897 z@}Jl4+x%q9(Opk=o%`7k(G zrThJkJpJa>-luE7+x%qy<`s`!7fgFJ+I7Oi!DB}}=su{={q{Y&+_i7xc+;v`(`)FJ zqS|$9=2fdvEgg-JAR2+b@PiLdi9VpOzJVjx(s%RW0uTCf9DO<%PVI`GawJz^1=rRT zJs>#(nLm*H06I+O5zBetw+*lV+N*ya_xPbVuy+`fRYc!|`=-RRPK?*CjN3ts-?5Bi z561HX@V*Yb?_i7{#?G2mGYhIwvqnr^%Q~r+jT_|Jw6HC3>d@v|=ic3J_8#`(-J#%o zl-q;{F7vz|&R)Oh=T&<)KHhr#+b4T`4m>>+a-#Hf%(+ruaG6jPrm}8Fs@yw~>f-GX zb@5h!%DwKRa;~0GImJg*R^cI)lfO@8=j~8gdEcna+-)k$XxX%4Ik{hJ%f8657>~0r za?bRa<6Mp}=3%`4BA;`K&%4CuUMfDUE?qsYE?@Un1-AlK!QF6`cPCmUUyW44A1C0U zJw69ZHy_^qbkWzV;DqTvf8sp;VZVqsNb-DAu&TPdPnp@41E+t ze~C8ujy~LmE?7>#PKO63(9c7SJm3KDHXv6hH47Es;y229;J+3+zsU;|U-Eox&p3No zaL>4V4_{vm+}Fo1HDhdwzVF94{gmr_JO3KgHaSj8nTRB?%ORBYlb6&p8G z#l=t878^fJ#l}zNc&bV;T70~h5|hXA30^8OVX8`!$FvE?$B8_bBTK!G zGy9*e*s=bJ+rsHTe>`dIPkn~>|FLbiPIqmcTHI*Rq+xNby0!A)f;3{F=qhHF{msm( zoS+3|bSml_GFD}V=He?CX}n;dxM7yX~;0X<)5^6@fX zZ_4M1#+TThvG%^;9v@_huW!Lv?82BF#Mm4U=BMJXmV6#IrCr*il5)1H`1Ca@HhGDPPMoh|;%BSqxartbFg``a#d)X%EI!UvCC0g_ z@lMQUN_a|$Xq>|v!=xM;NAh;cVi3=XDogR=I1aj znVYM;r)pKR)8@6zL(#KIqX-cY*g)aTiKMbHmD# z@QE2Wg2DQE{P|&UzC*>Qtpn$aRaC-U6&XKMMS=0?*eNO|))U|D0oL8|+29;Y1$!AO zuG(@^T~%(ni@Jzi%5YWr87}H_ri;qYc2PwaUDTC}ZmO8J=n}`5T-DXf80|IMlKj84 zHws+UjRF^SQ=6;0UF4>27h;^hh21X1Xm4J2QP=sb8`yO|_gap#x*~roi@%@Bb#Prd z$?!swyUIxPfET<}TH*|qmNZvoWUN$K5>s3`sxII1SGl*sRYFlj+4*F@(w+3rf-Nha zd^Tm`BVwT+9on_N!`#9(G{L2+)yz`R17Y;{Y0(43Su6QHrl18r;rHo9Za``hrS{Mq ztyb~*+(0=G{C4p8Z@Nsw)BhP=|B6xDOD^wyiR-Z%SUvnq6EvqIxc`8-{v&+7 zGq|6JPuc|T4^*#F{X!j!y0J~|nr5}?)V8R{z}~k;d^z_1)D?3cesy5`lS5%=N<(r& z%i@XG(@FwVMv0F~%Riu!vcFcbDJxY}{9F|gH$z2&^~g9+9(#a!H+;J*Sa(rL_}A1V zSCtONGt*pE7WlrH>8|p!+|=bc)dBsE9E!x{xJ=87i_O;3C&eh54t{BI6OD3y(*bmpG^-%Y(d#E3!6}#cVd3SaH zn!Eau_Qw*AwYjN#SKZVd;S4zCCfsl}$3+!qxvI-)a6+M)I(pgdRjZvxwY*`N6X znE#_-UvQ7#cnh4rhkvik7^(r@>oJ}hV={Nwj`7utaW)d%yD{b#;eWQ`i;m#y11%d_ zCK9*ib?Vc-WZ3vm?s+br@o?RD+a4bd_bv^+7+#iG6s$6?`KolZd{Xu|2D~TCQ{gew z!MvA>i1kpBG45dAO~r%p1S|=y~C6&4S0wG5*x9d*^#iga_)? zz61{>5gP_!~#s^W~8PXX(xX>KYl1HGK?uCg-SRCYGF&zY=pb1^V~ zk=B5J4|4DxDqkBM0QL)o55z8e(0ZT)JXA3{;0o|t>}gO zoVy1f+{t$%b|7ZRby0=HDHl^*bPgef{6P}A%9P{bn8K*C?}AR0 z&RW0p@o?dRZk_KnvbMTrUb`mq236wtZ7vWK?Blmv!*4ki9vA};^o9qdhNuZVP#dnV zLTsRUz>%B|0IMGw}DBm;wLd z`;9!1;{g|VXdcLe3-ZAKGadl*mo*0%@Gm@2h!x@gi@<-eZqWt!f8hdyCV&Uf2G>Os zXaoB^cSAS;>qsVn9` z9yDh7gO(lI+-cCbL2-?m=9zFn1UZ;f&&32T{LaJRfo|wKsVkN>ld|Thveaowj)yVv zvgCQob-;fUQeXVFn6NK;pYiZEn6HGHgLx~)izArt0_F#S`Eg+0i}~ke#F*buXM2p; zK7`e+Y3;jrDjGKa<2zH9&G~7E_jgYMiRt4Cf>moU3IzaRQ zCiuUYOFTd;@xWy<;{SZHJVRS>*&Qw54iEgriX}b}4v^S@R^kLxj8MYyb+JNsVgz>` zBix|9$>SSha;(qO78~pq?Hw_&ELoPv9!F^kB-wWJ-4DQ{){Q_$8wpKTFhQD`Jq0GTWfcYqL zcd^NC%*zwsV+mkB5laI9Nz$VG1^?pvHU2XuGmnnp|Fg0ttISMHv;d}cfKdxv)I4A~ zmk0I@dceQ~`8<}Gz>pUJ`|yG00b1dL;(T{-k8!Nc`1l%+uVT`cfcKIDg9#tVD^4v{$K)2`s$rIcxa8o6D=mFt@ zbo2mom&xQo<62QmX5Sf#QH531B+f90PMRJL-@#wTMYfBvvjug>?ZY#K{UkWEYw zCG#<&1^B%eq4Q*YfvimyEntr|fCpqxg$gDPD31mHTOj+wu0zNAqW9tb*Co~$>|+*; z3tO(|9eZomG4~<9k8jhZ-Q|I!huxaIc*cY6XZAe_yJ*0Da^67| zow^?1K3@e#PE|pX?wG6g_n~Ne(fN_!Ui^J*vb&094lIsVe7_0*$s8wxeXRlDfiyV4 z6bGck1sUW4H1^Q|*<#@SnGO&wfL#;|~kn#1_`x^MvVj50r2bkSK|&Y)%i9!R9FA)XwH)HtPNZctf8$Cb>Z9;1$6 z+RAy4KO8yu$L6gZZq>7>o6mZKL}E8z_1$$FseSZJy_RsbWSgAUVJce82EO7OiLjnFr8ZAg#;? z81b*?1vt(V{EHUAF6GF)paJ*NYBO?xT_;Mm9wS1!9f;{`dsrtL3$ zpz{aB5G7#RU|<=ZxDG#v2~P-y<=hR)G3fI=Chw=Ug??Ui1=u&fuS75}&&&ITb2ygQ zU*~UMW!|8G{6HQxNg32LC8DolW2s-H-Z3?IyN(GW^TW#4eZS?&X!r3C+kMdfuGA7z zXPp73hY$}O;dkH27?_S07y}3NVNA4QY&6C{NGxFD0G$&ouNnGJ!}nMH{bzn3zJET} zZwT(4upVH4B$y|!UwIj0iAeUw@K*Kt6RZnx6kiVul~p<#kVvO-;#Lgh>A*E z2hQi=?*;ex`)F4cL~I{|uMcAmOSFC@Z4~&AP89r;-^WDv3-;p#`*1-5ZDQ(V^gkTH za|ZoS3?LjJ8h}=`fX)S`gMXb5lo$Z~OFlr%7!OEHARLgJO-_JT^nfmNaLoGoH zS|EmaE1H-ff%?ewLTae4hO0wC$4cE7PW!p%kiPeA?QO3!Uz1zCdeta+KQ$6rE*vIGB!kVD>TZ8>R`1>)HD_5dk zx5^sxTGbCUY}_!oWrtRo13w*7;<I6&~PIY456gGmgK4CX}x=v;t_ z2Sfv8Py-+a)^$Em>jB{bLtTK@1IG9u7fm4e7cR)-^_QeY2jo#BbV^D; z3byhE`zHK@R}=nCt>yv2o|rK{xMIR1=j3_K3G$rq0gtZ=mW{8G$0q$C*p}GhS+0TC z>9|8U#P~S|?IC}U9Do?-i>19Nv4AUejIOGXnxc!;5M)xj93M-5mHNoo#6>C@Es%CK zko8A_Wec}1dpu<9M-OD(VO`6*m#Ujri-qeYC-5~oKsaD5`G8*JgoFcR|DkHK2U$4> z{HLJ%tN*TG|Bn*y^Si#qZ(Ir7*XH-O2KR08`Tf!RpW*jsvA$$uoqDy8+qAHWW-Z#~ zv6Cm4KaX@157-ixxkaaGWW* z0UnDE$l_RY0FQHIt^mvByd39Z7vTia0(tn&OW9)b7#}K+rFIYw&^%ylO~(a1E`k%p zG&dM=S4>_`uqZLZ74$=~<^=;z<+&oB6Vtx>8D|KVCFfwkxICxx4D!5Sp64{T$Y;tq zTK)OP_ZP|Ma!#y3a{$M1S)qIn>YTID0!i_cR5bh?#hRzYw6!X;=%k7%jw;)Dc-zxY zUB>>@x^vsx#CFUNRFAJ#&Fl3kdELnFG}E0FQNDfYbzOE|8djdO+a;EER4@$Akw&8)TWdK*t8~ z0NTKWf2k1`4!8(jG#3a5fSYH!KyXidP#`ga#0*$IuPuNZ3WWo7&cL7(1b@Yx6Fw*q zoNFu^xIpk)C^!Yrf>)ETzQVDAGYs#O7{i2Z9c%DD6OWiMu3P?okr;o!NM8r<=W#xN zQ?9K*u95F0>l^Zj2ePRjN+CZOE%U80tcyxss?zhnSBY05)lTnyPsdN4NKT-`-3C?- z3ZCPDEpULWCme;p_y8Rs`;*oq7f@N&ew60}{~fS>A%El993Q{s>*%_QqWO(}znx$o z&Cgn$$_vTy?QGbj!3EaerhPPiWXZyFOT7R+D`hMi}{n7dXf_uhP z5cwvJfBe1RUwnT!F@Jc1y9xv6;b2~Tzu-O+tVdxw2MCr8IY3&D<9J@;eysuE0?7j! z;sN0T;Q?|1q5;qaGCv?ZAhm%h)cs3+V5+pN14yS$XB~j>KnCYC(^(ggDQg1^e4sf1 z9=NFWfq@e)3Kw8`Qf~(j=y<_&PFh}{iwS4siB5R#xjfEa7QBHwZM?rQ*I@EkV^#ZU z;RV(K$#bF^OuQhxA?Gw72(H1e5!dn>d2GNpt?3w?UpD?d%}0WNeGb1bwL)A&K6Y8I z7afqp`ov^vhhn2AsfZX46+=9ba&eDJVXfhJ{s&9HWPM@#9-Zz|FH}HYFiv6t#);Go zPh;GCOg^v+aX>RTpf>eE6^-+P<@vxD0@44y4>8T}8?paBxUWg9-xR$set)15`-@nQ zyW7gzDxm!bow7!|j=x2&_)+-fh_d8jZ!o*T=<_|5Z-lGHzCUqg0Qsc=bbqk;essTJ zKLp$h{C}*JWYZX;V~Ax+m)mJ;5DyNSNdsG(W%*+CPJR z!M~VjcCmbPM*$r0%)q(C8-nTQO?aU|>kxw$5e_h!#F$d0=iOql;rmAfC z!Nd!fn6tP9MuiV9vkp+Sdp`Kr1{Vk)NFJg<9~*r=*p@YohJ2+Vhbb|M#yrnyT|z5b zMf6FotcB!nNSlr2u>Ls%4oG5-Fh)244Hp?Vn|)HYtMuz(>Hr)tahBVob{}-S)4 z{gy`TPY~?atygz1duE388PfOCl%+H79Sk}8B)KG#n%(^>Gf!vy!3=zooUFf01s5CgygIxisgKoS%Dr5;do z0&C>sFu{Es&&N~qnn*rN>i|B(&*_%`<`-i0De53aZO3mA&(k>rR8PqPO7aUh<1t(}vvL5-QN-sL6 z(u#e{(u)1cQj2J@bdKe5dXbOH%s-_vc~2(uY?+t$tBgy#Rpv$J-g7pqwCoM6U0Ml8 zEQS~6vQAle0ZvGt3O3ORaDnJ_i4_FH!Uv+?#ji_VQtNnlz<_Vj_q>Ml@|>L0ISf6o zVK|mJMRFWD;`b%@iDiO)SsR^2Y?Q%1B}wd0A{-C`r$@xjLI><*{dK515Ok#U^Qn^_ zvOmM^`VHz|Bp(n82ka*oAnS;wR#@f)T1cJnU*`f|%&;y0lo!wgI_Agkzr=6$HrTI% z&$lpQzZdg+pAqjbs9V48?&d9q~ zk>~R_;$LtdD7e@8e$oDhxqgG+m$+a2KP`2BGWRDu5RQL0%mWC{P4j`&01NKHvY2py z!B_`mJRien2nXm|0bV0KU@$ZSc#lo;B9@&7kIbN*&HLx07nZPB$ZD0CwNd-F)V#fV zyT`rw8T3(^xCP!z$>Q`{EJS+`4%3J^(`=3;mLUa!qYr=E;X#gH#72P=;he^D)5LL!{W39w&T?zeD2xY~XGJLUyG`^mAL(~Y|IYiCujQO&P~VsOtjCHNXo%?hE(`iTMTlaP}LL>qqz30sGDI``ys|pW^ptgZ&-NTeb)q z@X4S{b2luye~vs~T1k+KPT$IW-Yn%CI$2!^cU8VoE|@|0`wQ-keqZMJgNz&?{@yl@paP1!FIe1or=- z&NU`^k%~)OqY_wakc|IMEjR+cPg4KRdc&*1>?0Ians6oVNmzdDL*LBsAAC|nZk|r? zyLvpvr|?L`>AZs>C$shi97)}E?m*l(r}u|a(|B&pzRibM?b)(tKWD%v|*3bByO@K2z|Y&3Q4&acY0BanIf;`2Gxydtv~|ky0O!CLECH ztfFEks$lXu;W4vSD%vH9Jxaei^v#o@pMCUWGl%BaYSylmLN4Geeq9i>?)ngi|7jBO=&0FOs#c5$x3&fq#!q zTC8H3!%oQhTBTh2PNf#2(XaV|b@mV{j(!@NAN$BRBkbOpM8E4tBF+@-zHm5W`>A~~ z>%RNiXVsp~2UqOcuyfh=^&6LMU$=7UH>(y=%Q$<%re)LSeYM1G)|y41Oqfq>gTL+}gU%a&GN9$EB6aY-cCe*%KXIW`5xyCdaO`ogJNLIk$10JGuRg z6|*|eS-ZIJf{iPNE#A6v-0~f3-T8a7SMA-re8Yk5TiB=gd-m%HI2v&#Cy1>%3zY`NV8$oW-9D{&Ud+&--_o*ANcKmbrex zx@de%<~%Y)>q{&1AYfiUmbnq)sWkEcX;LT1UWM_@4TePt2f)j5=#iqMDw4ItOSZ3h z+-F$-A6Pe3XkM#EY>gV#j^IyLpaEpBVI2p6|2i@sVA25PH37d4yub3@TKDtYyezsO z>?8ZDHU#^v!TvzAs+C>I^{umWwDayad_dNmb@T6?PxXD8dd*)&kmL6YpRO*1xvBH` zf(z(lYq34gAj`Hdu;(->j0!Nu(8?B46#VlF{ukMKMo;av~m_P71hn zAoNuJ_S5^5)_uRtZ`HRO4=?#<{WlA@tXMU7{gTH6{BhrEz52JV(zUxoC8t()6 zmzke8ahW~I&UKD!%h_w@b(*(+>4%H9t{$~~=X$U8-+#So>xtca_XiveJd@~~6OtEw zJMn7VqnsO|W!c5Yb&Qajv5Gt<`~EP;keTXMnG=+Ik2Gfg5Xl8T(*Wh`1AZlnfBD7QaqCpvWDlUc>vs;@Ag-eR{)Ur-J+0X#6E$ z9$$FrdzFrFOf3m5i!O?0UxDxkr{gc&+#hf(Z}ZVzF=+9#i?^=cHD~>j71Nf_o$9f0 z`WV+ao&zS%aPBhRW4z-SmvPp^#|^9Z;jkXndiHN!sds;e_j>ke`BvKwj&HPX*Xs4Q z9a_ECu4C(0om#bcwPh=Z*PWbNu(zr88#Xpp?=Y_4CdVc1d*r(-;Oi>Mey3vOzqFYi z+gLZIF2<&!wRPhPwszLT}IYLPgqY~ zKD+hY4NH5YMaHfBe)D{|VAuZO6M^Saf-XlF#NJQ49$A`w?E>+^UUG+Pn7^0{R%QOt zlNg@bM)0lc8KlJ|uFnt-kXG<+Z1u4b`%)VreqY)Y@Sj9p#f1L^_8W_godo`c2dEcL zS<4<{LF!1@$tn#U3ERR8QvdrrCz#>{ zd7rKklzL%sA1c0{y*^`7$<62NR_VoOz8|>jeZ`#MsGo5>ka~s=o}9O?IiuBk8iRAWcY)sD6rWa!aczcG$Ht&4G3--0nO z_b_Tm+enOa4KYhG{?+Hbb>W7ZSPhAdg&&&PG^uFY%;sHdYpZu19Btq2*tz+8@Ii%v zBYRh!;5EV0Yl)}Bf=x?%E#I;3i?0stSh?fefnz6P&!-0GMczuf8uKJac;Mo-CDzw*zMSK+_-!oL#9|N7hi!1(=>#Qpp(Z-IL=@NbFVZw3Abqx(H98dz*;)45&XIFHW@zd614 zQR4L&^87nlvolL$Uo`)D@O}Z)_?MU;y>HO{%=HQGMf-zWZ9EqIYcA0K-QeT37Qnw7 zGy$(64iJ<20Kxt*!X)#tv)LXGo74-TOLK>e&>JEL{roHicIJ&N0j@%mP9f&*p;N7&Fd;d3pFoS^vtoS+e!zSqn~ z?seYytqz@r&xm`$zsctd?vr^g8Q-suX*K@w z|MA3O(X1N`iTV-_aMt@ZrCvIqBH2%9;pXK(fAC??yH-sb<<(?wS99|kU-O+k_>OYV za!2~GK7CoqxL1I#3;frIarrxbK@Z3nf0pwj*Dt!iF4%7l_IrZ;&$)NriWaTyPY?fW zWcI2(U)`6rABmR_Q@1}Cz3-uX!rhb)^ZSB*Q_jzTeS_~e`Fum2j}iA^K16eXK?gj; zyXXOV4l~sS3FhT9Bv#O|gY5H(zYmX}r{YrAlXE_d)(lahxgn2F#+<*m^~BzsrQfU# zoVjYjF1LAJb3UKqJbIkl_-?~S53%btpnKhJy__ob=+*4)&Rv~eZPV8A&-V7#e|r73 z-^pIJf3s&bn3sJG_1=cGFX4}0!`=jw?}PU$j9+uGDtj7RF`fnQj_BKVSQo52Iq$yM zhu9!Y?os*?Hd2h|r5!;#92*Mv4#N7w3BCE8?pQ}YyET8q9*kI{5BReU>WZ%vT_ABq z)23GMST}9_rnODuH@o+=d#mr@&K16#I==Rdm9tyS-@J6tx_w{G+5=)- z_jAx}S=`?!DQy)#n|(g0XG)`fQFMM9*w&bb3k2KR=Ra#rwNiqA(ftPPpUm@#q5;U! z#)1D>xH}>a4Gb@f%LQ9E94i9ph@-zpU$H?7c(IuR8d*0{b1gH}(i>x#u*sv)SK!NWX-c zYZu<~%LsZZHNC;{OUdz1QRm3@`>=lRJh8sS{W8yQ$oYYNnd<|$GUunU%(20LgJtc{ zc`P_LnxX#J@EZMk&dWSuu)H5V5gbd6FnPVOcyf9v2dA7!HIXE_dxf42GeEgsOXz=g< zpv$!1XDmM3&ln%|7UQ%6c&rLG>wwdS^qJhF%nAH<0Lwk$&kwO-^rPIbY#jC_=7LQ| zQ+Z+2ITo8~wCTKl8pocr?wB*5^Esb478}WD58-e10Vmz~yKPNeApR1oVd4Sy-hH!0 z3%l1G9qnH2(yhsxojz#s{sfQFwY`?jXvzGZu|A25ykE1@~iiyIDZd(wBrz#kghG`Uo>R*hgZz&Ct1sSW;+eszM|WPOm_Z&0rf zEYAb}gOT~-PHW$9n(Jo_R%U$G!}mL}p0A%-)he!*Rt-0I?9n-B(rowQgCWO%mY6y` zX#+KV)4;x)I>(&PGyF5x=L=5td>>d30I!BMy+%w6{!JVJwoM$MagUET)xL$@pF4DDC+khMrvE>5p=kYQ_b|Zk>OGDar?R)9#y!|< z4o=&F*B;ma`cCd)@EPU;e|lluyU1)IeYpaY`x1YJZNj!<-(cH~wvE?q#WrEx)AS54S@V&~SQk!Rzh3q=dCmwN6FYL*wF;n@S!7!T;0Xqns7 zT)>(LctP;5Yofut#y)sY)FykcNNpu`RpS4R78&Qv`flL>ayqf|sH-@keA5C-UFUl} zY}2`|Xn26_d3~`9;lEK4FuM*mo!AhxW%zbNzmL zuAlup$jxaJ9Bb^m8(Z<~CY*DQdHP`Xq&De%sSgN>Xa6t!eh~NX2xiTxjG?%+O=wA9 z)}BXlA8zma%z@?1ST)aQ;&j)wpL{W9+~7|Jbn4QxV*^L0MwJ{KEZ(-aZ}AeC{}cW6 zceVZB>Az<_pE&d-u=P6Fe+RtDe#W)1hTyR|xa@%S1oN_&`B-r4im}IGl||TU`fn3C z)1Bz2{S_-#JdB;d&Qz*Y@f`Qvyuf*XETCegiUAcWRPf`u^E~#(PVwGj*g-yL51+Z6 z&;N?Qvz)&-m*2<}`vM*qiQnu8ZX|c+z;#-IojS&RVFg)VOD^X%Cnx)tZEPC6+T{c5 zw+D~vUuDvaiPp2%&hNi=-^K;|{SRFT&JDYfb0?-O>+*N(o3xzuQnHSLwGOO#ND+Mx z?#0Io_675X$8ye4J1zUI$iAy$Qd23lma-SK!~juP81=yc;S*ILIx#wBol3bLt+pK7 z`DD=O;rG#$muuBB4`Q2W$pgq-fb17!%Xg^BwZ8-Ybsq5V{6^)cUQ7>YKgjs`Gh_Nq z@%@Z93&vhc#^FHLZg{e1*G~3-j+woF@vVTI(9&e;*n?u15c7K~Z)*Fz(fjAXy$`L- z_Y3a*@%dun`vaf(f6)O3jE5L;F6;fYX&&Ht+CVYk3?uG?^q#*nsoTNl=TX~oBT}7D z4R}P2T+!?`3q!t~?zUz4XP-_B<)Et7 zt3(n_#S!Z!vu|!%o%(gN>NTjJD|6xX8(QYou2(nDyiU#B>gLt6s?{({M{lK&Z%N?2 z(fkHse5OBkmcMa?{dT|QZ*Jo6FXQ)`&h>oBwT*7=psrY)V`Y1#HQ?%$l+d-zk;&ME3~ME#58j4l4mWC0bUH)|G}sHL9{>Ee}}Q9 zeLs76bYt(Xu??&&RSx5i;5djJy-5QEf%`zgz7h8(J}{g&ZS5>;G-znazB2YN<2U{le{bp*jeVK_KtI32STy;3 z+^u;d@a8}tb)}C6fqA)yfd`mh1U5HfdpJG;cKvG9tQN^V^pY*D8e})NwZ3fU)V$cf zty4*>&TVgS-`!i?2K2hyd+30BeMb!Xq3`g4KXf11=ZDUHKDgJmTZcPN?OWfnYh{1K z#@@EX%BJa+`VB1dYYI2isG7#_5r=LFAtv+YZy(?~w!s6-xwdKWz~}J55UyGF*pL_+ z3b3jv7?OCvcpr|Jgwq;Z)qbU8SL?Tiju~3RWsbYk!c9xYY(2VT$MGodbZUnlXBC}d zUDI0d3{a#u*M?Rh<{yg4F^OBx5qf^JV0Qi z7i+fGq5&ce8er70ADY-&=W(AmKQzDwuKx>otQ+6KhHI}X8b)dX%JFX+qA$K>eEf;= z^BQBjBI8W+>MU`;;NRf; zCGRhBzq|4^Vm|=fYyYn~z`z3<+uHYwua|SuiqALrdwGxO{Q&Om5kw77So}g2pTl|* z)|L7s`#t*V`>%_pEuS4Y#%=t%0V9Wu`k+sjHjb^D)pc-c_&&F#mHSOep3W5C|0|vK z4Eu25tAc&-R2gj50rySmn>OIBH#U;~aRux6`%1*}mG@E8?p?FCd3gPX7Af4j=%Qo$ zmRCCW?tY`+$ia6UyOr9FAT)q~DDyn*>7ID$kn+zAEOnme^^kqTuGO`u6Kh`6 zd>?&2m1`fswcGPM)Rq{ad-v|;^8o+KNdDJet9?JXe;ch?opIe1-`|yeT*o$Q+GrJX zwSLr?U143%lf?YfU~(Drd|t|%e4jTy|19k}aPNc9KaX7y-wy|9?XUef900ZhjK@I| z4+z%D13c6D`Z;0-Q@%j%>%+bLB9mAzpMOLpT#YXKKIrKE1zVP8PMqO(Wcb*RXY?7^ zqhH%jZJIW3(ZH-xBg;1>#y8^rm)vXendUcPAKc454C;X^8!+F2{uu!7$I(C2xp&Db z{P1q}x;%@HkF;)Ko7tvI+oGO>`*Ba=(LXrP^ZaS(wqjT2vf6X;kBBOJ@xtC$R4O;G^*gS7Lw8lzT400fx2DtcN!F zdyR90|Cha0WR0~c{+DBu-;W}H$uZ2V4gCev1EZ%Wt7$0IpZh1N>{ly8PAuWDooizae?Q_Zi!D;Q%Kyvq~T0 zXQrS<_kB8PT-w(sca!srRuQS2nCqLRyvg~A??1!5?paLyzxe(O>Z#ycVw6U7r-3J`?uAd{wMIe$@_KbpiWB>6?l0;(T~u4jD?3s8WHK1(gx$mBm zgLypK_49`x4D5ZMy%lewISQ(oS5GyoD*Nx79fJe5@>|a5H=PIv$ojfYV96F8P+vGe z{Hcipz}27l&M!4=So@WBotjo0F@A)V*OKXj*X&up?z^C)35g}~k2Bc2C7yjrV%ggy zKFI@<{S}RPhX>@bX#ZF-VgikSj`bc5I{yd$(RX3&?G{4)w}0d$w(K-fPWIv|NP5-FWR56{w8DG zj4^Hn{yU&6KV}cFW!#^`Z|1s1R|9e)p2l4~LJjW{=K7|pGvSjB*k}K~v)rHK96n$B ze~z{P2gm-*)fsYtf^pFR9<*ZMo#*vAjeFq=9!ty+<-vXdvs852SNKOi=JP|JuK#{Z ziR(Pi(Bb1otoxwf2OqcX*v7%uu3-%ei+b-!ZeGSPU-y^zd{Zxp@Bb5iQpfuy?1TFj zbV*ly{&4!m4eT#Pckg1o^LcCgW{KdwVB{C0?@V94@S)tVjP(R%DL3Nu{j!pBzXLN{ z;IK8UM_S6*pGOX37Q8l%`OB%a+`BM}`8Us3O4Baw zhqIQF*JmFT_C6BKON<|j&X@T?a18CNTil)f|2@7tZR@qsL3&|JVG^rr*1~ z{Ywu#%lk3TYckGl$y@iTUB}!7-@l7{bR_TeKJ*~zN`wke*{m)^&Q@oL`%kmB>vV*N z<^XSez7Kl;0(ifGZkJa3f6i%KYab66NNdEs9E)^nUlQm4<=Y#5KVwa=a|AQ8 zpRxA)f_?hqBVv3HeEw=;)9)KvHwtdmseRU|fFDDd7YS$lM^G0L$-HJHxQN6jMsm-+Nc>`CEPH3Lr)D_E z@>D;fDJsvu4#7~axTiq5dP`yU= zB*{UtWOF~?WfdIYj-DQ7;sA*S;QY$=hPic}CzV)0IG|qL>aRMrv8pnB%wQYOMbk#D z*}rMe={TPP_VOr8Vqc#a?iU`znt@p624j+B&lJHsdoXLwFS=j$S&%(hWS?ePbIlq| zzGoP*p6LHzzOU^4;uq$u{252#iK|pnaiIG4{Gq3V$Bw)Qk7a}Z(`Xl|1^9?-lKY3r z^}f$?zTVSUEhq}iiGCWFdqnw1 zFD2*arB37fPow$8&M-c`(E#U!1Hk@yqxrHgtS>wuF@SJ@uV9_lAOG)9>nG2N@0at$ z3x2#mfV}}Dl31U{*uIbv{CMG(WyRy%Klkl3q~F5!UE6-x+}@@!e!rrPO`|ttyc)6p zt9n5`Lp}>1srNFcFY3@2&A@&a!9H=d2l=(t>~nCWnS*U)*Zw^&jrEvxckTDve)i7{ zEzP(UqY`rWk*8P!>^Oal3EsgpO&nMq7-FwWyQ z>hzy%T;&C<0`@tNCHMoBO=_0eTpkMDju-plz>r(Rv} zaZkl7@|ORl$p%wuwm zugaQ>U-g5Rzxnwdc&7as>(#)16Y5X865D=e-`Z*O$6t<%*?N5cy#)MBc+S z%u}7B&i@pc7dr!Pz0vHV|IdMGAAH>f(E{M!m)d;6xv${g$N_?PU-9|U8hOB%&+un& zz|e$cDk=Yj3eJmq%3c~Jqg}=Y_UPYpajSMM``I*W#62V%R$v^+z2r^#{pY{SFFDq0 z9T;b?Gv3}uGuOhb!M^NoBJn;oxK-D(cimBkHjc6VMh+>OvSQx-z5d6aBwmkG$(K(M z-+JU_THsWbsQxcKypF#ENZ;jzDx63 zqg_UKnY&@}!X4+nkBVo%A-NZGbnNGs>TzvKyYzL32EL&yas zTso*?h;7LS-yZA!d1UwgJy$!maTwINNy8>wXGKiz9s8oNuXBD9^Al^ze#WxFi4_{C zEqyVFzL>{DS@S#x1w3^xC@`QSm&|jzD2SoAp%V= z<6iDdsP91lrsFVuKaw==sVyA9`kHu-6WGf;fi^M2LnR63g%7}eBKveDr+XM4%j-C& zpN9t$nRAzQby3vQhRHlynAFs=_A#0@kLlY~&do^W&GjwZy5{kKQ6K$i*UI5Kbw=5I z5AuT5(zX(Ti-5?KA$pi_|hF~cASXu zxtxA0woKxWsKj~T-OGS^ctG#N2Hp+(G8tNZ&nECLt!e$1%=rY+PqH4|hqb{zp~OZB zt5rf#h}w8`&y)Ti54+RIx^XJkdVp)44*vV|8#M#})ynGt{vnQk=@UBdkH4$TcyEBU zF{@@a!ls4ovcY3U1g_lk_05Em^s>l|z3k&NPn`_)R44KMCy4z|g8Nf~eN6oSS^U0% z13cjXPdLCsohJr3uXzAXfELi0rxjfw=cQKoJUkK@HCIJteyifI#+H3^X5ak@Q(cpK z4eIx`Q(LD`8#ira!|z{(-1S?=T;Ge*f7U&i$@ z&$x2W<|l(c`{Y6M){fU%XP<=z2;e*J2G{e!mE4oFCmir>?_FaqKx+UvR>s9k^uz16 zEgDtpJFI`p$@8bVZ9KfwC+K4I{fsLC%vr1f*Tf;j0a5sViSxDIXOA`6XHDua~Rht zYl3SDPnG8Ye<{@e_I<+hGXBARb#%WqdB3i;>eQOhx=V*`pHFp9IvnBmFuo{M1tn}? zu5Sjuf3i9bUXN4rC-^@pIRN5O$pM^&1B3^>g$HPTH1-V~-~-;p&g1tDoWL=@U$F1X zxQ$5J#Qc7^`Y!0yLzlT;IsJwW{GNIJFRg8wNbaW^-2XiGrB?aZ{?l;}W9wBgQ33qR zo~Cw;_imLcR~n7KoQL1v*QQH{s85{77q2_C>tS?Jd|66?H{*E~^HmhsRw>C|k3C`;$RqNBr2#v3UulEVvKlaR_TSbe%UbpXh#>Q|NZVzvuvI4S4{`1z_Ocg!^+~UvdNb9Out5N2u2c zhEHK^tWZfse(2WFCsS9>FCI8@sCS!AZKvBdx9QTbQG(+};jAyuoJO|fibxt$z;@W@G@6|DPy=fmjYB=$Hs#r)L@=B=hOM~P1( zeukVO*GlLlj94me860p@#TF-)E#Cgs<31yX+;41WbA|gd zCBkhd`Q97Q05T^axqw#aD=8E<6Mc>E*L^L%L5x0my-vMaW*vKWb|CNh1v=>D`Si#; z8PqjIp#ehUXQBf`iOQ0ra|t_KKhmU^6^5L2`hM14~)g$n}-y0Dr-N|2uE_qvU$=dlkTbJvhJ# z?LV}6OZ%lCeK9Uz|9cVx81av+0*ptoe_k{e!~K9_zE3;sEutocCB!3p|xq5%y2VE8=wJ8%zqMyajyWnN7tZ-S?!nOmeA&r-0C=xpst=}*H2!(r zEA-8~jhk83?>}O2Z_lOkR_(lSG9~$DcByE9(D()5d&0*Rwy*Q(gU2(|v$fyaG58hVS`lLY?uE@KX`8~)#l>#JOj z|G$IeUv^5*`5!nGm71zhBHcbs}`SI>tJ`BVhj+xIfPE z3Ff3vXx~pQ0Jzr%w$D({ayEL3@)qoiuQy^|9-rko%@eXeVCXdV@nz46>+x!v_mQ8* zOm@!h)boRVw)VE4H)vqlLUcdBx8(aw^L#I=?&F#7=ez4#XU2CYbpJ@!pUky&us_&; z#D!f2$awYqkfqisKNW3S0-%~|`_bBi!*pHT$d-TN_u`kE5oQnm^ zVg?OBEBF-+0M5nNC-9mi?%5-FPvkv`U;5eTVQ|df_)}yu`bf!MQl66GY1BrXKgRklEpwtD(d;l)C z;j_JWP54K9{~3MsW{nzV)mn9I<@l-V7ZaE5{`#zMR_whD?)MpyumZrlL_~4nWod{<0xo{;ubG;E!nEHyHa>u_o9DmR5~idJY-*{frH3E`}9m zK8?;jMclsxpFa)kPodVtLmkDA!vPxq)UTbyPE)@o_!sP-;rMKf3IDXK7BK*Go>Wpjc z`|*LY&Pi&W#rOANuH41Sy2;kA{riNuFIjlaCq4dY3iqxEN!UQW^K`J!ynBKN@jlq6 z_9asLeh>V=(eDe^Gnf=e(d_?Pt999 zkq@ks!uLN#j(s)wlKFt%bh*^oJ-Y{wiFY~wXSm{3_9m!AT-31Fkp2Tbmo3=5FT^J= z`A$w*OxibKe?Duts9_YmgZ&_lf9g1;))VaOb)fiunF|)&OYZj^bHv{8>1p)JsQ_{T zp$n9ZH~*Z((y>!K?zd>uDu?Sl#`VqM`t%%t)B#JLvV0EkITZi0!{5vA%J_d9zi%$F zf91+m`Zl$*n>FOKao%eWAG{uWEwd~nWgj{Jx#~!$mpU8@{+atfqVeykIp8=N;3Rk# zJEaYL1L+daP+{FJ|lzB*XTYV1?z)S>+fxHtSjWXO;4SK^g#{8n`|c$PXuJb4(+e;5sL zggTd__5SW#L__>Rd>Qs0-(9}9J=Uj&xw(t2qy4rIKN%6T zWY_jP(N}ZJ;>dgYMK5BGaw@Sau`TOf!l`?YOvS*ww9!};_tc33U|ZuG{L6dA$4gwG^}f7cI7H41_Vsy= z`TS^!LEwN$@^4aOe>RYMy9n-8ddXXbWQfD_2A1fN$ z77bu7YXqepKx*v-|HKI|f&aJPd+)vKXheqrqegx?chkC)XH%l@q_f^3B!RskVrIet zo^XM|wB`rvx(<}sK1(SLQsrC8D zqG7`Y9eedSHEI65eDAd6Cn;B>s8#!l`KcLYxcWD{(Ijg12~PUM_(P6824Fes;%)XJ%I zE9Wes07B%Pr2GBey4{%Z&YAhvjQ`kgrE}g>b?e@~>b|$0=MD8%mD`R`euLQi5AcF+ zU&#N}?GWK18+J+(#%qk6WF1ppK)p@tcmx=fBq?Bf*lzTXl!RJ;F#C|VC)^9 z|LZ(|;orw44k$Pi{DZyU@SOgD5Af78&pp?gc~28&tX#VPSVHuv+{;Dy7<n)Lf@cQ&&lg1xgw0jr6f8jk# zK@hh3D(wEb=>6H6H{#2je?Rhc75=SrR6PJ(3l{`}ah(IezTyHfFSvIIFK|xshT@3> zBoXs3i_pSzQ}2DXV%fQV!-pol+P2MVbpPAfpDoZok9F?c`FCP_{+Dn4a1D=$9V^nIx_P;}pDdjjhk9NeNM900e0K5y<^IawNsK8Vs?U$a}yS`?c6j^pnTn%;v9k>o(+r@4j8X-One} zcBbfFav|$@I97sh_IC&W!U1ZY2X<2=63wxm^Ch`qlKbHr2a?zk@t}bCFSUT^nefFf z%~tNHUw!oGt%0LPo@N7-M3;RcdxJ<0K+^{NKO6{LpZ*mXaOeHBr9P;2$Bwhce)4h9 zc7NX*=jq(~xb&l%f5KADCvlGE&6u|j>os_TeGy-x`Vf2R|ByRKbxtMyku-=;IO`Xccx^4k-^ zz8Rfr&1Ro)(ffjVFfFCc)u!wKuxtm1A`Z?g-A|j8O2DaL)s27Q0RSxxu58F@h24i73Sr0f_=FU_m;Z+Uqsm)Vvisavxt#3n$WRf3CtnRJ+2AA zuRD6=&YPp&z5vFu8$JKLKR&>6>}**lK=}ap*?)1by9wUz#{lUsO<1e6`_S=Yzn-^s zb69Yi{ZbZVxN*tb$mdxA{ss5oAMA_oFMJ?=K?LoR4 z^$)IBoX42gfAG5-@w?j~Z#~Z%Gra~5JoM?JMLEG4$+t6WlF|9wmERxZoK3u+wZ;Yi zhzUJ!&S8HsFm277i@p~gpf0!wEyCP17Z^5`rO*%z!q|Nn!|{tr$oKN$a4Y`JFO zp!*BNR|bwAyK~N#ZCUZ<1$QM6!(d&nc_+@-PBQi{y5F0ecijfS7oaX2;0x~kSg*zp z4iNFD?hpP0C2oUA_ZNumD9+Y67nour76L-aO5c5AGEoFxJjF;RVG3!V3zQbHOPBZWS+Z{e2$byt}UO zU%)wdL0#+e7w+={?dqJmr(EOutgDp&Y2&*}JW{wv9n+@16Lvrx<82}6*)Ya)>;-Omw8}c1l+U6jURQl_;Vb< z+I<0(iVw)~_ltAc{@mAGnO{F|+tw>@j(9txRhu@RyxS@Cw@%pp&&oU>Y|UR{ooB(n z{sHG)Uo zD0JgnZ*$5qa^JS#{|HaqZxg-m;s&Mv6$kLW#72P^lCS|H*nh_mM@-YXllfL@nrDQO ze1LZ^wrJft2OGc_UAvg~A?pRoIE-3%*QNh|{UH8H81M9I`}UJ2PXF9d`X;-|&!I|2N?OpTa>Z_K*I16707C4}Dv|)?xZPpM2`QEg+~`#$V$zjuEe0u6dcj zKA86+|IHhW`-oU|5&R1mDEvF%0)=YPzdJOMG5;#7=AUf8_wn4h zwcYykNn$hGb-dfraJ#HW^Q4S9{Q|mQ9-4|@$!|vYw!fsxlVcJ6*u@2WUU)%q1NReduq(U^Cvc63@QI5%6t9TwfuA7UVG}+=_lq5% z`zfrgl1c29c}pSjGUuAL7Ah08i2U5T4`n?-vdagi(gN#6TH> z$T+d&i3#=t-T04|@3um-6oqS1;QymJvoCk<)hoT}i_H$W{eKz%BR=%U2L1m~JmAK^ z8v8>J_V3uU=XX=Sn`t3IvbqauuJA803*j@y*O{jmn#g*s_y9J1024mI zyscYr_82gr`o))CO29^vT%eD@e{ZnX4Ez64S$E%!fB5rJo?la>J2AtFL*993$084p zEMtDgZONC4z;_Bk|A#SdVocHTuQ&i65bPVo{ugm^fEp{}da?g~!GEyAzfs|T>YO>3 z6#knv^K^Zu?tG^gWPC{Q{|oeyhXU3Apb!6w_xK0mI8Vdlt)6MrXi$&-{TF<)a8a~h zit}pfnKXPi4{*B#{DXV&FZ$n&eQy^BD7`PZM||xIC>MZtm!L!aP@nBTc#mw`0T4ILozs9A{(-OgUVga}ZXGxT1 z!FeGi*tBwN%M;wQ|ER(tC2bbY+lpATTtslpIr)r(V<)AwtGbk2pX@r8()qwSbzDrF zThw{o1`z*1IKkoK6P;Inv^5-jZ~5Ogct?0i7yZ2WE%9l@-^sTLj%WR0ab;7X={!Qjgz@5!`j=dN=nLKdJm_74%?8pww z&A9C>3c~+i&06qtv~aQgQ}F{*q&^paV2&1wgb@=qh+rdxP%8Z|<4DAZeHot&!A7(c zv-V>lcFC++7d!RrnSzek&Ht9Y0J?(zMh*CXXr_4(|E=NiA^nCAU-s3SwMpT***B7_ zY|P6b_HP3J=zY=u-mEhx7#BV7<^f+Yu0-0UPN_IRu#X5Y$oQZyxo3g#i@{KUmQ;}^ zxt&-03>})yg!9Anwfk`$=K22F-Ph1J?Ah1pU$H-bFFwIfMXr+^Fwp~UzW6&lpkn`U z-XFof8t;7SnP*0#^HzPbXtBvJ#c@5eHVvPDCvm?;S}gca%3A>T7r+5h3J);H9n1>O zt>DoH29viFX}0AL)|2pyAr~-lm1ZrD z)z?=MRm7|GhU*_~#wUnm=tB0~`Lq%tI z=!yj1Yx=#<7caipt#99Ca=V1rC9g}?_-Ts%S9{gU-u1%qyz4*1_kZEsF zEa1@$j#L|U>w{9vx?749ga@P!c15J!C00ri$4|t`{eRk~j`_U9eNOI0-42F@M|9p1 z4$wJ8_(<6#51bQ!MCSnAUJ+ihz+&qZ!37?ng1T!;E&i)jllnF^qV0sHgEpTut}4=mmNA-9$!&(&z9q@gq064zRCQMkrVCU}SxYB~#wvrgu z_uhd1Ux@w(|F(r--zD&Y@PHiqvX_O3pIaQ%ML6~s0ZWoE?2D}#mb6aoUvtuubobpa zXVi9h{dE)m=0^0ttU)E~P|}^6KSlj<6%kR^7_bu7CKR>28>yEQH1l_+zi%Ml~ z0=9ip;UZ+AmQ=J*Gb1MICU9tyW9n8)3#GZ3vV>zw5gY9eN;{=Zj+N*?3;K0( z?fq-0+lyHPi+d@7Clr4O4H<+Z(m`vb?&lSFgU_wNIa7-boPnmzd0O!N1^5o-NP$|B#3KDEL?F#NY&s zYT35!qA^pahHVS*tIfDrhW~V!x#G(h*B9Jl1Mn=3$@3Ktgu(-1@IVL>M0_QX*szQb zNhvmfPvW5ejt8UU5J@XQMuoYzo zq$|=3=}m0<4Wt*>bm!WR(jVZ7m*IlOvMvNZ!k?CJ!$Yb&+rv;|s%*!tk#?BW2yzla4aTU@2JXc5O;Q^K58 z+T>W_AN&fY?_=4;0dh|8Z+Bg%*WK6Zd}8Ms2cH*lN~w-@`#`ur@c}-CVA?6?Ty4tM zfD3F7@Po7o2Z-1TxIfr8Qx_W`E)828e_LXJ);wR$T9seFX!q_Ly#@^~gIEnb`^7w8 znTsL&hCVH}81L$j4ZKJpEE%^BqFC&i}&49V#K`PhCM)j9`4s>8oz7E z`4#(L^!_4zelTxeti+FfseD}`btO(p#NRE+(#~gz{rkqu)uJ38TFQlDZT*Q8w+6rS zPDRVst;5OdTmhEfp>Op>+ERZ7oVUf#?1J=RzR3_|H1+YwMD&ly1g;rFA05K={pc6M z9qs6=gs)zCR^nCo1!}K*HwVyPRK5ehnVB`xeerYnsR>79Lxt=ap1sBVnF3QM8Vq~lAd(i*Rdd3uQ3>rJOl!Q<}{`a?hpZ?%q%?lTPZ?ON} zQ2trvuX%_6>B4`9CNI4_cJzlIZeMlyXij2T-d!^}I)SED$l zkg^!eF9!2SDw4cd3!ok#B_j5}H)~M&#V#QBe^9fXF3_Z(^&dI1jQKtWbh)fUJqdY} zdRMUhI(Y9(KYN=wIuns;$XCcL+UALHehysl4cC4O54^``N5TXBu>-`1XobFNBDR3U z2HgGt{Y9;1Pu~>+c!}}O-rf84pYhqE#la`7mWx^Eb6Ky-6Z?OumXM`m-&C{&+#|(H z6vjs3Apzuz6DQlLOAyO(~PaJS>7b&OCIS1`dN++c{rrlXi9kFvihg>IZu4`4B z>mE8+=v*LtVK39U!B(Q{dK)@jFfRIC>|>habbkHnBS&t&IdWuqi&m{dd7g{F+c1F}w$(E{+sAp{&!GOBqyKx*cg8n* z;f49V1`YBdW?USSm3AlfR6P0p8_4C?@f`+_8nLA!SRXtrZ6O@6kUc)L^byGq^@;z6{P6jzpIEE1>lf_uxc=tbZx=8x`v<l9uhb_Eo&G8d?i6vS0OxfP7@3)GWKlVTR-P^Gk+=G9x?E|hw{6tcg z=#osCx&#goadClg1Mz=vV*mcJ^tr5)nx(o>*_nNY4J#$~7mWU22JR<=2%DxxiSqW{511Rl+YgcI#e7i))z2IHPyrb}1qX=jBDRX)-$vakwu|^Q`HOfL z*yXI*qVS(}OiMmfSik1T(VN6W%SplrAqF^~XDMrev;lLCcy`b7tef)uUc-iZi}x{& z_qXw_v14PGczV_(R+ZG*a=ch8VkP3?~zru;z7R8>?zXUmrB_?gAT-(>B%70Ac*t?^*`9s1V?;QSlD+e%~?a-4DcU?d8O z=Uf7QXAEU1*ZN==?1u+7a1Zxh_oMM6B$q(OiC)52d`4n}GDbk#f4cEc?7z>zQKM$g zT)!cTyuYhiXBqoT-9_w=HSBW1KiD&YZwn%rSM@UX?nNwM*H#YxDXoHaMCuCfr+zFI z4+!3AQ|DdR+Buds5wQbA+`Mt07idf7dZ*X{;%gLuZ!jMNcB0^8u~VYSvx#9nVkPXG z6eH{Ju^*3_?{9$z#BZ_kdGSkx3&c;c;M16pxNPEbjJbyeRTkGzU$*>G&jABc z!2fo>!vwxdd$>r=4Q;UfADROc{$(B)_HZZgKN0-z-Eh*UAhA62UIMy0fHgn8Ecox@ z`=b{{d{Yo~y{}8}i@x_u6WtHi{|5g3CH{x~9~cjg3PQBFQ+f3>H*C1pqyNA{i8F)y zHQ@J4aQqQ{>r=2j7o2ZoF5+=wl94EJXRG$@^ICW6RNA&v=Ton=Z(q)4040o9=04r1 zk&|nq`K&j6(*q7zN;UDbh#l~Nzy%5D|1ffH4Xg)}lzm*YpD959e}4nN zthnW?t-Zn9*F3}iNE>XcmwB$TC)(?X?2GhS85cqq3L>2nU3Uzz3n|g;4C3kkmz55Vk-t{y-qt21=}$HPwCN$O%YX&3BE` zjCt9NN6xP8`bM8b;*qQQ9ukXaiC-uCe>V934+($Xr<(f<_M6C9_JuW`-}bu@AN4@m-wP%{L+@8>z8SM>F#o==AW@t3vf#o$La6_?^f0h2~1=Ud&U%E zOLG`!T62xQ!@90bgG6_O-`VKpZ}G_&F#l`^-^GtHA8Ysi1BwQZpHMUI)6XuEmwau? z%vsmp`Rwy+gWh@PO6R_P&$Ah9)pLy-=aa`{AFy zLJoGAd4-Bmrh&Dz3uRhDWzoIZvVz-*RmHcHFI3c}pUq%RqLaknHu0HdiUZ8Wd^fQH zTq1QdeocHfJ~=+O!6a)ju;&i(|CI+1UhhA0Q~~<`D9>&>n0u459nZ5Z@2M-;9}M=V z@VpndY}4l0kns~z)*LxagpscIne6pPYkWFuN5zE=86x z_iPvA$$@Y~>WC>*s%NZReRW4*;2l4Qtu8D(tu8z}wJspdQMV^7@xiMH2P;0TymVgx(S@mMsH1_{|=+?bVqlzqkI`&cnOYZ#sBKf7{rX(TQ?iR zaHoU#?)B-;cQ)o3I&{6YB&2iUWiP_)Un!HDd84WNo8R6TDLp zNKR&M{eqo4uM!iT%1>$w*i?;u%THL% zw=*x~!8be5{Y%mP;Gg$y%vq|1gMTCVH=+x|M9>X_e*^dr!8a5R2o`^lKH>|%`Na_{ zWQ@pGnNh#l+vg_Z(M9b#bvnU&lf8dl$9`)hb3-NXuL1uL#Z!;K?e1Kd7vX`f;QzgM zrcFDr{)Bh2=>Np56Rg$e!oS%1F1-7tFGtU>0P~cH!aTV5&!Ub5O386hw$$MXV!Xbr z^BtVDg8pvO!g4a|K3%xzN~fN^^1+}1-0woxk#o8Oo$vqVh!LsG`KZ|y9DF;vD7(&i zI!jAC%|5%8ajX>_tEHW>Y3ZyNo=}=wzun*e-lVU-y4mf`ewXkGD)9%h;DC7U=LHAs zpzqFy1Ee31;`jDNx=?QqH*_R#wJ)*d=`+`_^9xK*yCCuZ_;gP#3j8M&irxqNhzY!# zE5Ux{O0X^^BBi_TKBlx+$$6a%>{Tl@$9)0&RkR~gI=R*<<$aOk70io7xUX=9#0!#$ z=NZYp4rLC82^&4*LMeV%>YatVJx+f(Yo>X^r=K1jKW*B^_rLmT{a4GEAJ}lx%N$Wu zd^zi42{C{ZT2#tf#`&;iitt~=hmqI?WfFW4i%%ZTobE7UypFPHEw&`D?yFU+F7zBQ z&_Yi37M{}-p4}jxRX?ylgl9StzyDiw|0eP#0+_pzwQS$M%Z_uE^=9}Z)Vhv*Ui3RQ zpD~B|zu@194lvROM8dO$1IP)D9c&mz*2=P=NX$_Tj%83kUz*UuyZv2k8I98@zl=lTKyTDf|=v z^I=b3-xP3+UiZrc_n9lfIwB;NLlU!54cvuYmm-#rhE#xAvC9iA^ z^U61oyK;b-S(KJqTZElxuKRr1^4o6=9eU;EHm%RFE^Qt$0WKZLRADp1?%@wP_|4Q&rKDtO1QoRa{ z|9EVxS`DUk(d%;EO4a75Ud=w{t2BEx$EuV%!hNK)Q7T@6Thuk`bJXDw6BvymJ{D?T zh%Vp7dYx%J&+NP3u3cL@bixE%x4wNm+jr{p9h+fIYSW>^IDY>pAI+M*W8+D$l%z9f zZlzY3h-d83Z2<6ZDHB@;T~C=*xCEXfCTB+9+Q|Q}w)2i1cc*;&ZB^Iay(1WJTlqrc z#*=wYBe2g#^DN&N>@#h8-3u=^JwcA3ed^q~yeI4347fNdc^A2&%NW-eoxe(t zVn6ol8h-D$$W&xJ{dzS2dt8TZ-M*YUd)ATtQBmb7wT1O2(V5BXw1hnH0`9F9#G)!! zgZ(wSR8fj>Y@@VQvrbj@I#t>!?Nw{Ru?Txuxdm=;aP5C>mo|8V`-?c>0wszUh(Y8n zVJwX?dNX!MahPU4T~@!)W6w46VG7%J>KGtvmBVN6^Lqv(Z!(rLy#L4%Grw80Ht2*S zxi#Oc}m%alP7MZTsTFno-se$TJrtHzF($A z=C4o^kq7n>;ejx>zyk(sfDm{#|`-Zrk{L29R zR)1`Yki_N6K1i)`Xz}Gm^@~0C-5D}z(slaixkim&D8UX$rtcczn4{PNTi}63^y{xF zKjHU(!Z`P=G1ETA=JClhRTSKD6a^^1IS$*!gpRj>VGDRxQt2uM^Ddm*s#&K};UDY^ z{?Dw}?5Ed~O9KZep-q)w-PI=NoKjajv6{M^bMeI){j*{dhlKYs8Pj?%U_PGD6W&eAtIeyxfi%jMc;$>hGOf)kO!d~!m%YIi`Qt8CCtkyS*t}BtwNtHWnU5O0qz@=$T&E8JIPfh&014l zzwX$vyQ4mwdZTTZ&KI6<+O!-SAf4YDOJDZocY1=8_4Mtf)E6-RuzbLn(cdrFy_>yG za&M(n#A-2V+Zg{}175I2!L;Sf2F)r`g`D1?*%Zb%fZ6rrGSUVI2oE?Ah5wp$ngg+) zq2yXe4OmB{O~fhfr?D9*#ddJk2xrJ=f8-47Db^m8@v$UwCR29vzf-gwAz^ohPMma} z!ORTS-Sh+VGS2ZOBKgoGI3CftSI;k|&7XUGFZfSBn_C~Bi9eIFT8qnJy?$@SWSF)wH8&d3e2yjjS_nxJa45IiY^Gl zR}95BG@ws{;ea6g0Lc~iWqrvI6Z~2f!T3;K-RDb}oW&lDeYI^{$w3|n2i)Has$spq ze-8dd|GV(tg4lV#o&yKYn!a?oF(57FN}8ZJ8wvxC%em{ENADjn(Z~xqNT7XEN z3-eOu>k>>oIDegHC|IwB6t33-dGG%8H9z{X=znJw>&@c-EBs^sGX@vcrc{#$uE{K zPY%h>xSn|~O^ajy`B;8uQrTMYFWBFx#71eW*@SFXrM+e&INk(yH-p{HV4XT`cCK-_ z3%L8%xUm=c zJ$d9v7Z<$0N^OX?Qx+IeB{*l}sB#o`OM?BE$~#5UYc|2V|@9zkH=+I`@F;t%J}zIY@t z?ymD}z807D1M7M5p7Ae?_~DcQonm_ke?;%63HgD5b zVgjX)V++u)6Od5E_sOT9I?<+MhmeV1e35)KA)dH7bAWP>X)#$FwZyV@;D4iL0pC_I zZ?Bb-vFVyEs%}3E=A|y@q>iXM#}3sdJm5MfC7*S3gHv(AW-VFDv$WSD@Qd(=le*Xi zQRMH05c`WE_UEWh)%Hcj-W&G*dsi9fD92xuda>|+Df5z*5`!4-r=`w%+ z6l-m2rCm#49aDvWFc&8}nP(MhCmwI#Kwg2rmQq_(zbDLaW5V?5C7pZrh-1#r37*RV z!98<(y-AjcBIuUdYw+OmkLJy}bSN(BuD!N^xskrC;j@`|AGpTXixkWkuhk-u$P&1@ zaIF@FL=>&njGPNcLizupf_=mw@nMN2CojV{nh}0>A(e*)KUL;s}faBmI;Ra#=G9TbR{)zu(r`>bmKcD>ZNY(%f zU*h4BZ>cK2Yh|x7BYV{w($E|9rKnTnu~cl-qQQK$;J<|OLXqZ^>ZqHub<3S$lis}n-&`b5pz6tIo+)76+EgY1oB92* z#LW`uZy9TUICjo)x~R@kYQXp2rX>`Ck?KvF1&oW>&LL+-wrbXMTQys)2*)CvvrDPY zgLUe{7Y->o=Q!u$2jK`8SJ31%%+cL&k6j&6n8qaCad0@@2y2;uy1+BI`VMA zq7!VgKZl7m|58}z__#Uo*2nYbR}p7V#t$>H8)5*@B;fI0@K8|^)i6Gp(nk!9=Q;tgOQDP6B5LU@2a5H1|ey9_T{tr_6- zP<(=5@E?$hUSVv>AKnhN?I3oVLavGF&eU16%X$xd%a1jozd-M+e1Bqp|4Gi@Z;)TP zJ8(n!P)|VNpFZ@&1NdJ+>=^v#UeoP=57uUoSU-M#-UcOs1sgP7Hlpu!35ElLi#BQ@ zBJ=^b7^oYt1B0+P#r`+A;(zFW7ye(*Z~jWFhykNV8CD-WkngNLbU$1-b&;0gQ?_oo)zS6GUIr?_xV^2I$^ruJvob&Xv&t$Q_T)`(x7T1L5 zXWh-L#cs~tkI%fB9Q4ha>C85TcWdo7&0Ir#x)wRVU9%(Bv)eTb?QTA>pWUX~<=kJ% zIga_9UCMLYz_>1o8_sXVXW?@qHCuHa!3KygSq;wkE!bct;*#dm1@+4g9lYCT_^_+2 zxm(S<%SKNp;?o4f14j|hSKGDQKk9=If>#_qn3Gad$R1`%%(?JluFrlrZ6EWRcqZl1 zTJpKV`eR9Px27*ye5T*|_#{RH?VRN{T?^BtCjuIC*`VWUNs!q2>eDBhXEKRggovR*TCKD>Cn7LF|thL32# zKQu^OSjLZ;XW>VDHyGU?$6Pae?Wy`@o}QP6zxS>k9^2TwWlLG>qa`_ik28Nm?LkbB zm;IX>{vQ4bJc8Z$Z}gp~;QJOX{l8G~AD$=p$Nm@n?~s^3c7Go6xPp!7{*9E>752&X zF5Cq6k&=y?p?H%PTC|x`cz}8ddYpG3NQ~G2zW*=re}(_cto@TqEGv{bIR2{-AI@~t zmh1XIbsOVI=#EnQRt5V>BXL#R!1;D9_RMxLze7pF>FruP*o+60@wCOB+Jc`+4hnH- zi5-Rzmx^XTS_|>*RQ9Df?l9NQ-?{zHxKBU1*>ljKtFLzKZ~U&E!(xN9?3pa z2cjcyXRwEjsmMo*&fcaaa!jZ?`JHgU4$VPnN37@Ef*zwTVnZAv za?OREnv+t*!98s7fQ{1Wy4KFUM5JBu0Gwi~1UKYYNo+kTZ8y0N$yx|G@zWO0zt#DT z-k0I9D*S*v-k$>=h~Zs^Jm0i&Ah}QxteKs>Kf-V}rKaFs2EIcIahK$3vzB(2F~L)L zbvpumuT7gb?+m$qg)Q2&$>15J&*0n=YMB^&9#2xsT?~zP8!V>rNu`@SHVH zVj{=L?b}Sw91ywFo4#1TLUY80i~E^g=k-hG7?mVGD#1 zBM21y!_@&|ui1GnRZixUo9{4oQPyY35Z^Yhz4m4E7A<7$@#h+J|HFg*Z`?tx{pZ5} zE6jy^3;Tb;H>=mgV*g+NEBwRr0pL6cYzGx>QDtB;zF#3WAf=&%J|S4AG?a>L(!wai z=nn>TKp=TTg8xwByRz2xPy%iWrT&k8>% zju0;3SnlC~3#@0bYlxqPFy|_iIXy{*-oz8~wEeNsb?<-o%`Lw7m8Q*`pMAPfqcZ%N z9PGH1C!TuB`f|&bX7=()e(&oU`HT1Ls@@S2cyWJh)K$;O@M{~rj$NL)VeR>GpL|r^ zeL(-RmhIXUHEG_w_~q8E%U|o>{dB)kBQCu6?N`^9c<#P^!eOa%oG&B~F+z*XB+j3* zk-0nQ{T#9D(evOus$wf~*=<^ktBis#BYE#~E|O;#K|e4m9?b?Uws?h4+XUNOM~wJ8|Z!broV;%h2O4S7mxl| z_>V~OU|xod^WpawgL@IdKUfdu-G+eokkYN1p-h)hDbWL=E)o2P@$Q2pZ;&-aL(S{a zLpF|+?@gUETkYwD|9`S)|NgsXu3I1Elj=O3j@=QLbyy3}qW=&>h{n#3IgRdDxCf75 zKK{%uFi&}QmzF3}vr9{I)e|Vi7KkBk5Ke5;kSaW|f^}x#6XN=&;vm)@&(NH#Q|Fgv zt=r^%;@-^78}5F*VE*kdR;{?RJ3REBl7zTyOV!w@WX!Y;hJHPX6<=#l$%anzd+| z&z`;oecv8lI%4vB72`kqq#C>D^oS|%oo27k(_Q=asczrB>*@AAUa#r#)`0WF-+%AQ z#|!4(SaE3Ionw~Rd-n4s#0Q*Oe4!6E$S&6HAkLq$p8bN^7ln5oRRQMZ-BiNORXd2& z?BHEVxee@Zg#*C;DN3me2b5t0h>eI15J>Jw05Qk_3uA`Ko?0?C$#GNME$orPe#3?y zYulm2Cjh^bJLccu_dh(?|Hd7DivPjn-7o%T-TK7v{QMhfHL2u>c`(Kf{&UgcCEL*T z_==S>Rk7m#I*d1y}PQO%DXDeb2&Wzo}mGS@4BQcfqHL>r(dED9EVBZ4{ zproE~c8``+>ymSOv_vG~tOv47aYht&XgK;x;)w>hEQ~oZk(qm0+ryu6c#D>PrAV`! zEo8oez1}O?QWsxaRiApZhB^M>_zs0@dsruc=ZIwo?ur*&T)J} zKX$+g4ml@nuDTq%ML5I3eT6sdaDs4z{UXQ06^L+w8DAq#utN;SK&;(}Eh8M3emP$Y zEljW9;Pu13PnRyb{r2SdZuA)P*42)^dR%PPvBUY6?b@Af(Z1aoVufeg_vlvBx$hfi zdkh*_`^L~A7y6BO`_iEC<1UY!`oXmiX3xAaZ^xF~JA?i22IV;GQm>uTQqQL{KOjKy zWoRmVo{l}0jU5mF3%+BjnIl=fQa{D!i4(dhkZ)Zr1S<{BOX%->!v#^N>=6x`8qn z5$uPS3;w~n;2jC$*eD{$^xZ(28wmcxtef#a(=}Q9chdAPYMIw%WzLTWbIoUuobvv{ zl?V2F2j^r~XVhkre{-BUWatv&4bk}iG1&KUwY#-Mu%1x6mpU9knRwn)OFFk#OFA#& z$?-lo0Fib%h6`fx3!?BzjPO86I=*YlI`kRG={vRPoWsnAGmwL0B}R~mU64gCT&xzE zy^k0KV~zX&+_JL)}%LrT`rCo$J`=tYVUCycN1ov|N1DwG<>>}!V_c@&( z?sJIZ5zfIWRyaa9AYS|_Vxop*;^^1`@p-J*Q)|;wuay&1Pp$U|54^Yhfal$>R#<2I_y*_x-gzH1!n|N*H)G5~|&iLZSXG`bbnzd!a?KQ^^-QCS+eA3Ky zX814TN}=XFYta&leVMcG3FhJQ4D>a)4==!`=lw=kV}Die=6SNlK(z<>7uk(}2KJHY zirwI!XAKubRj`Lb`3^n@5AfNrGHk~D4U7fRpV%8P#I{~b$o1AzFP3UMg8Z+LcbeR9 z_|P3~UwdsFd48`-%n$s_8mIQ7SP$h7Hl<et^%@*dT@4(L9qebQHQ*}ey26ERn5mO-dPT~EaX1#m}(Xns6%rW=3UIFtWmk&^v zW7aoR0$lKeE&d_;7zeet_e@^``{2dGOiV3GcYgsR93xzy1DLPd_^T6EqK&B*;i)3NE`J+bq{iSI=7-ear4d=1b1EWAq@dq!jr zHW~Pb3#5#O2coMy6!s(Gg2;+pT0}V<0RAJyE+i%#%=lxVUDgI;eG>8#Q_ttIuB_?i zj1|lBna^~zYp>p)FxRJx*!{?VGS64-H%1?B(EWc0$UpPcPw_u&$oTQA7wp(+k15W( zm0X#`ehuKCSZ_c9@x8KL`1w1*KXPg(ct_y)FhuIAg!_&1-Ya)&M*4seJs`2b0OD5x z39JR@ZqaubF z{66&dHv9mI`+H#j3;uOIpMb7U5`B+do_GPwgOx;ZZw5b>ONTf<1pYY&r)IEg0(VJr zEw)1}_G=Vj$}q@Pz9=uFuC7q zei+#6tpVTR_xB;!w=prlKe_FG^nb&6|IY;YKlqqh`ybu^EYgm-|HFq)oVa$;ZVzWn zasF*5d;f&9onq7fva4!M)%;vQl(_hZZ64ADs}2 z{~jp#XN{>S;)xD&$2Oigd5bx0Rm7>I$w6AnyxezL-{sAILx=YHbkV~1@M(AZrl;hm zUn;M&5qpkdEIuNam>&8h?u^*|`1ykS^ZUW)0nKzl!Lq{h5%8$j-KFiC;9PIB zQ&*z!e?)!8enl_UHFBMtbBX>roj=@dcHt1k2f`uLmAxTcKwa?w+yNJue$5}n?*BLO z`5UnR4-outr`>yhsJ(x%|3?g&IC0$~50B)yvZC9v_m`1<@B-6`*9!i@d?>sgdJ27k zgerdbAjSs=$h+p8=>15s0TJQmXz~dhXY;gm$4}fEFluxa`DT&W z|Es|NMCP0JXy378+fg5U*lp&f4U@M9`W`eCWR|C0Dyeszio#E44S*c_Jo+SFI6&-u z!Tx#C@yG=!1?&28lIZG-=wq;Nx^x80e-Cz!C^0EL&N=X@_yA0rz?*P~Za*AGkAvN- zN0mrjuGQQ0b}Q%XBG>fNCf7^db!_KY7uv4q?drVibGGZ!&NVJ>ahK{o!Z|M95qm=M z58qk0AEe~_Vjq}^c_fN24);b9pEY8qg_E-so`XM=O}q^W1?ORcb18F)!{y>b6cSe< zh7(bW-2jKjRqr8|im14Wa3Z#Z!ailKZ~&N>@~mJVsW|}l;RWFWHxIx8(IVA*dH>h~ z+}DV$XvpXN<7WjYuZNe2bC8deP+e5F{>0Iqa0)Zi9{mG7NlQA5I+kfALl;)Yx8ydj~-Kr{#W>C4*NUUeVv)( z`BL{jeVR@DdPdj99^0qxG6Wtq6lR@DyIfZ1tc+D0U=$80#WuzN6dNGvyy$#veZfAM zF$*@(`zEj@rC{Fd7TPRIj)LW*s%{3G7V1{;XaT!sL`v(mWB0{IU9J)Q$~m1Ej!_;1 z@021xt#d4G_pg=vUH|bqcU{iw{6fihvt8kyd?%X{7w6C}m={~bDjcSMgYcK$CjN~H zTPOjWT(BI+yNKm|#lX$c=yWC2MK8w)*0JA}J%0{=3I0sF0QbS431UN0Ch|-b{?E}? zdsuOR;6F}r0@y!GU3fs@e?NZ0K6L*+-n(FbFMVN;7A|&T0b@k?D8b|n7!~#t$fYc* z+v@LqZrc1gNy8?L-`K6s8)KN`*Gl4jg8c{d{=fXa&f9SAp`XBC?fpXz{K(;xCT+m} zPmM1vye;#(!=2l8?nbwS@=n7ld9PqUoPMOCcYmw;NyeddFOfNnsiy_>GAQ- zQ2!$#`I)6@mrCm#r{kHo{yn|``lT3KmH!_nIt#mBVIF*#z(kVZ0{tr-V3racYPkk( z1jpdY3dStgj&n|MeH=Lk?m50LqT^IuE7xE9LE+oNIjdZQSgEV?lr|C7_5yxJlaNdka@6E)YJDWBvEYZxv233C}6c z!{(8AhwuU-*cKbfjEG&7#5KZ~N!Uh7*hq1=p7A zF1bo6$2Y*M684_p*eweCH)umF*AP|8wX|^$>vjE{etbf+scT*5*gZKFDaDhqjA;15!h;9c- zX}XBu6D27*swG@HO8qE2N}Y3w=ivm(1h^p{PKXoRfVTK^-)ph8hwudsgMZ-w1V12F z@J|^<-w0PW|2D9X&&U{JR1W^j8IxwJDXiNW8gzd8@M^z|Bc{(nH&GaeV>-~ zoh0_#j{Q3sk9hj?6^q(0+Oz$=?ZMu=ebQ{{mh)vdQZHnZpA*EG6?0n)i8)tb)1r@r z1C*^#|2KgXlMC;v1UsVJbsaC*6zreS%nHMTMe1PBa)bSeZVF~8m2gguZ%R9BWM3B% zynDIY^mA4@FW6OB=Nh?I+T=RdJrzGtw<0zXxsSS+-0uOQO*liHlY6?k%Ed`KZ}EHJ zEnS2IbTMC+c7CVuz~y7;d*K6JlBB%EImEh zJ1=qw4mhlMK=7~h|LOh2d(Z{=WrjS_`}m9GPR10RV6Fo>gw^@?c82=ZPG7MoY0T8g zo5=AS|7yGT4cPyu!1!n1gcE4hO(Fr4q--2Ua$&*!t)=;Q^)p(Tk!-!MsUf9NgTLx?o<%nqbt7=s1@)7k(abB;3bE(Vqa-PpBK5$*@<^;Ws|0~}|?hpQj zoAmF;|EFEI8949a1HrzkU-^*-1oLLjo3C;moREZlAY!I2oTxZJaRSGbNpL|TrPu(9 z3k3fn7vKO9xFC*F@ULtEe1bTa9S}_%AO_x#sK7QXK}Qf@3?*kWB8#|WX`q&TDVw>$ z>325y9k2dk#lo1eA5B^N=I~)-UhCRbu>T}HFZn$UasHnT;fMO*gM0sU1pn`__vg+v zM~>w(59hA6IFz|w;6H~sJm}Z((~K2^bt708%p1}55lAGqpWt7xFFIcddOw=jd@S)I z$pZ-j|ADMAAI+M*)-!n;draQy5B`b&g`xkKxbZLhb-MQLcE^8z{c4+bzhh0{$EM7k z)oj7G4FlGlI5cZ-Y^a~1Afqhha@pO~i)pNB6|6-U9AaD?yMw+caq1-O+$6Dk>GNVg zi?1#EUDxqo1Ni@@Eaf6#5)c=z6B4Rs;P*;4Q+6DV^?6Otl_uxl}SirlEdpH4J zFu6H_QtBqv2G)fK(ETRi1!V`&CLDkW2Phtp^1Lo$2k1V4?hC{c7f|>I`_b@iq{M>@ z(ff?ghG!99%qMPfI)NPL0@jPP+*tL!XYr?t=Y)*;;N7KthYcOY{N%PRSi_rG-@k+X z-wOU8suwqW_~$->2l3yDU6m$``S8PCYmWVpXA=Au8Fc*TGOw=^ey?Ht4c{*ki8w1I z$KXtKK(zRQVEw*eBgW8AVksjS0}f1MeID>1mqC1+J*HM3I(W0+h!Lm2f2iOeT`%*z z+q&^Ddpo$+c#=5Kudoe%*JI$o{}}hp^yg=+U-J6$L!MK18hku{aGD(PRRxzau9h&a zXd*|}SBoh+tVNgLdsHz-&36;uSNHkRf#Of2Qx)D__!6v%Ev|42b_I*JTY^g$W(Dt3 zr%gm~E%;Wrmp1A@Ud!Vsl+pOm!HeMrH!@EnDE;7ddn})SKe=d9{0C6~20qggk@ON(~# zKx`#BD-s)^ADhHSe$b{CJh|+9g=fL)Ey1J<=eNP=Z6D1pe4ud8JXIGDNGUvFmFv02 zjdLB-oKv{huXpo_x?XGo*Ezf3pU*3MLAXKg$vNHj(78ab(qxg8zo~{2mU=KjYJn2>u!Sm%YE5AYF*Xzc=pVX`bs&oGeTN|K@^V=4Y|4 z2l=3(jO!Rh_k;ULbU-v^H24?n#}F4*x*oj8iMZlL!UHj2PVxZV_)pBnHmxqGU+U?3 zlevav%`iOCFFW8dd25i@#>1wlnBe@>3j2B1yt$YG4@9v)kQepHY_JKt4;G?nAh6udZ?NfwBWQFL+n>fRwJfvIW3AF##R>E?YqC0MYyKK_YQM zB^)OR{uvig_{Ro_<^9HD-$fJqjVvX9un?V}PmV`EIl#r(Q}Au-6e?8qeXv_$Y&b_+Ld8PHM@4ozU^Fi2fUE%px;dz!DX#SLNK!*+;WX$ik^1d7P z^7@cko1=hGLW(FIY8 z2f)ARdtyV%Hk3G#j2EerI%CSQ^dHFw3{GWjKK89l%t1e~hSk!&dvEp~KD-S59|Zno zkNKhW3*i4Z@$deXLIIEb=GVXZRr~g@{dUms;eQzZ(c~vST0E!4oNem{tUBg7W1GQy z@AsA{b5MRtW%8wBVgv9%6+BSl&E8f=w3t%j)fMEjV#CPzm-xhH*EoQR<%vxW29s-c#B^^xw$;EE-C8-Yfg~2O|Gc;{*em%I{u@nM`GJYyRaXt zFfYEM=nhIH@B$c@mC8!{bJuVZ%zme*pbrF8xCGc4FI!*0Qhj z-`m^CZ43Mh{pUBWTD|gbJqHi|6LVb~f4X3Hhxyw#ja+$n&+Kg>UI)K7M_U5(oK=oX zMR(IKrW1FFVLe|z)|O{&-!l64Dal`xycTkX(0O`Z4;WJM0f`Cd7~EfKBwh z&I5v77k&lTv@0Hv>s=Uj>wX>Qg6kjgFC3%$0aEJi`t^FdJ0_sw0(=)0HxL`Zt@l+d zK;i>@SH%N*96;h3dOVQQW&cYYK#vIb(Dy{ zC!eQ)+>QeBeoFC+s*K=1O-nkHS$E75ac%8!&+5;Y&dVGz<=vRq`@P}&O54^4UufE7 z2Wt_pXHD|u$XsmA&w0=9lIJy?cijv8x5EZ_QTQGX_)m!iJP`BwWqN$WT_1b`58_|; z{_27L|7iTDpB~%hA9TucDwDncj1`NtY#ltALiDx$Ly|M*TH`=YdibXCuY+p-l9*i3|*VQ`~&>UZ+L`j zeuezHHSRBe^ytxO;Mh^E-kVjvn717jE^>wpW^K z3vQ=f&ei9?RRqzOPrwEA_cHQXs;~)a$c1B!D~aD`1{;!lqw;t}*9!g>*1a_01JT)n zaXkj`BkrZ$;o|s{8;o9_Yfq^8dj;@%|+EI{_a~@_J**=ZZc>PE{GWFGLE+;VNJZr-X6A za)0voVzjiYIa*S6=Dp+Ah$|bt4pe@#W>Mz2k0vMd9{grVtM;vZ8o$`Y`)T$GVUIvB z)*_WX7LK52_OQ<62KFaefF1A&?|TgIyN_@H9NvU?{zUus?f--K`L7Lrz=II|Me7Rx z=zZDyrv*hX_Wt{9;ul|f?FS@B7RD43D)Z~-Zrggj_t2q*to`MSUYJRL7(jn$ zPG5L*(4axT{X4$kee84X@901OfnWJ3edIbSl1@wGFQn)hC~`(Wi~ij0WmSb zk$c=gkOi~S%XI$msv2eB^o2RXoHYuun+3FpAP z9nm>J*Z*PzsIdg`0r2-#o)F(reNWNvD(^`j2h_*?#pl;^e8jhxJide)a(PcPX2Up% zU_P=4DTD(A^ToW!GH>=i4JBXG#<~Kz^JpJ@DvJdNiIAAp#FoSr{ zINsMAGUmfOd_j0$VgU_)z%PpV2X8L^zkBbm7OW%Pr){TBGbVriO`vB~%vsz2W$!JZ zqe`>&?=^Erd!)T_;sykQ1`8J4-5dAD8h6*=1PcUrmjp_s-lq^R2tqz*>8qijbU~dY>)NHg^AM4Ew=9b24)2>w6^j9>4i9HeJR4 zQ5EkIPmWlweEifbQNkf3k?>2R1Fq1gD*T_|Uz-!8X!5d3W^7phvT29**Wmw-ga0Yu zwl)5sGjkrlk~8o(@c(PE`N(hi-am16KIi|IL2NivPKY~yQ(dl&k(Q2C-8$9utlPUq zhc?6ejvhXJ;*z=R7Wr*Dyy@(bh=Z|LvMy#PJ&b>p{m$?#t0ehhEId{)eevh9b$aTRxwf@BPq6^~-E-`~V8XrHMxoDXm&KYB# z?F>G1YsJ28`Y9_G1os;^;%KXG9d_5MU)$f&)%mc!lj9}ls;==h>eWp5Y}Po}tF`wX zuXZi(H*D4HUd;w|3tZi+Ubl6yORZG7QUp5S7&>4b@7ZK@KreKFm&ASeaKZ(AZuJ2^ zKB%8w|4W{Liht|ue|BWH_H5d8`l#7+f{tl*Pf{MFvjcB8b$iUjpx@^TzJJ(9*pGN3 z_(vBYs{OZMU-R@B{P9ub7;)t>75~=!-$m^zMRzj<8=F}?b&byp^7&a#E-r^T3zIkp z-hzMOgZ>8oRqV?=82p64@cm!&|5!y^*%{2&AjYhZv_Lv=#(E(AkimR?sGXhth$_{q zk8SAPcoN*yoZ-_at(vuAUHeD5|ZvVmc=}ST3wOuYZgB58;63*gd&L6i$CrHo3DoOlmPFmlcpsQS*JRYig3V_S8kYEP zx-xnBoC5>L4PV!~TZe_U8rGfXQq^UZy_3W4DjwBBz`U_-&(62{jUE1Y{GwSeXRKTP zX7OwW_-5#|@vqwV>Hf^KNyB^2)!ede>}=vHR;qZM_iQyj+gS8|XYgN_ysrbT z0N+}_6-5Z)WhcYtp98+zHE;g$J69FAeu)uj4M}^fL_SR|DM~K2lh!h{;Ch>DF{6Rka zSOLBB_|%aSKfoasPrz<8b&=lJk9z`1JI@n0l<&&4N$yNkoW z(cZoc2NI$B^~sM-CAz|^MWK1AZ_XK`9`6Y|m$UKYe(myo+s@D1y4r8@vN?+fPaHkI zWABawJ)3y;addSWT-mnbR3}#_-@1)GgWL7&k~wVJ4-XgZSpRxg(1{O0=~2b8w-QTq zcauw^^K~UB<1ZJl*uCZb;2+1n_G;r@=vKYz4O@HLn97wa9l}Rj#Ct#3qWf_H%szm` zf1h#z|8lKVihsC2clJnhVD|qac7H?%Wo5iJF#9(&WhXQ8e5v~-_p91^!TWJUu#b<- zYcuY_IXXaWehj#e1^+RK;9p{Z5UKx9VdtEVowv85n5k!Ax9j3(jl5eL!T%o4z!+>m zV+;O2|AqZm<9+nPZ^8XvvFByLy&ZUV2iJ|jcn3J9{>V6F7M#;^WIcLq2iM)m9=Y;0 z*^jlE|F@c-G2h9>aY{9h>SMgTn+)wVpy#j=(AJBWbC@LPe!M5-!B=hVY(jY-w&TN1Ar6q*FV9atz`q>xAOC{V*?(|< zHQ0gExlQ*T%V)0N!0gZLH->vj%9Yd|%*A7t6t>KYf5ChN*f--J9q<&KWBa2^ZU1o< z`=SG)U+{Ho+ZASk1|%(2f^_Q?{oMrizNQw9nltxF!xk;{%pBMO{)b}&8uI>E79RK) z_*eaYu>WV|8!%rPaRfO6%_hn6c}E|O~$U)S*YQ1;pd z@_&x=|97E7d^mrz931UOyH<7WQ>$K`b}c)%_8vHPP|Hb6XLg#u#i!4*y}o161@kwZ zIpphi`Q*{vp=Ylg(}kp-H$~?L!yRaD8D7La$b6$K%zB^r&{UN2IJH>+IEk5-T1EdT zmicy~A40LUS5zzt9<_G@!KPJLkPFE>X_ovGrPmmFUHUO!&ley44Euo3tNw?MpP@tN zB;t>#_(lh%Jc>aC23y1T34*R7o z+wB|6-x!F_+xO$Lc|H>sPM{riO<`wiVuJeaR1~3d<@Zl>FeKsp+C#)U*1~} z2N&mV?RxiKIcKwPVrb6ww<&iLc@K9myNP+8#JSk4U0V%`>k!VV)y1Qs}F4*y84jcv<)ZruJAp3V8_<;M~-j5eB!cy z$l2(_ahFWzQZ;#(GUFaxxsmiVB2WJ^vcT{r_O9t&-2L?T+Iyz=y8EX0+WW>2x(9|L z{o|ApeIdF6?4%SXfL(;EUcEBAlds9wlZid_g~=sJg{j5bd&VMu&IhjV=jJTJllEa=Ih&)%g`ON;?s}JtDxoF4QYqK^iGyS-HUgFq!(;|od zIPOBfu|xOu95!%mw}HK8bne}4So`knJ2r3E(z9XH`p%8K>XxbLQA6#^k$4Mz`8l|+ zfVkT`+I6VsRew&8p?w4AZd!5cqA~ovsZhrrQEEY%^AW;a^$-*Lnvq~5k}A7BmeKRY zZgX@%(!G?DO(*?7bQ|2~xqBUtTpQcUk?4S3JpZZ0b>aim!v@%K2EG;^;G=W!F9pKC z=?_=&Pwr11<;Q(|C5 zbU@5AUaMlpe(dv8;2(K`P9P5u%)XHGI`E&kNijT3Q;uunKJ@?LhX=Ll*N^6X_W}QX z@c(P!|Es;fAHDCt62G4_@)vA2Ypu#TA)eUs4#==_6)G$wZrKgKgFHOzrZj5po!hoo z*9Sd^egAae#IY|1O&<4Zz=Tn+dJg~oMcbZTo_V!y@u+&;+IO6)xCjrG#J=;3l`2)- zMI1U$;!y6T757nt`=CFi>NicBHT-Lb9&P^IZAgzVdJOLK&6wGvEC0A^c9l76m(-iT zWqC_5(0$Iv<-_Osu9~uF+uCJ|{5EV|?!W2qiaow(S031YW%Xgds5M7-Y1bX!mAvWn z-qcNJ_M5f_9Ld-feEQnaxS+hVDPecd8zb)rn4%sArbR!zkQVhYAT8=aKw9*J!1UPr zT<={lYHpv=hvgrRzLdQ?ZaM9#yJW?a`q}$90ny&q_QT zar%W3UnlM=z9G9oLcn_{yFJ6$mmHqUE@EyST&zT-u2+nALzvePQ#^IW z{I|pb4;`ExjXeJ|=-}m?fg#v{=5WAn(gQ~S_g^f2;HMd2{~85VI{T0Jwib(4dNKQR z{lc9)4N>_wnEgqd&d&Lu8_bU*=N}=q9-D8q`%!{_Fdy^mjEaBJ0a0K+<~g5J@lUPF zEAbDGD3_T@d5+${5O#^A6lN{h{ZJd=C46n6ZB8+kq2CzG~T}!{eHsbqdH`87tdX4ytUU#-UP!(1ZJ^&lz-- ze6-aDS^E$hHmv=-=AFI%*u=ZZ7fstXFWaa^W80P;ysNe8(x!38-kmyh>DS}?t^<0G z>pr;8%pSw~FGDu;96n%cpOFLo28z5u|uw~V; zC4TFUF7w}bXyv{w2UhRjy4UC6Hh*xv)929kZL1G%+q7bz@7hH>)-7A$yL!Q_b&F?D zSw4HtxH*#+4w*c9X`j)9*K{7()3r)otKsPybv*)ItGQiqbao22ce2-@ z>mta#g@CnyiWSQr<#}$$pIVCEn~2`)!{6-<_M{%qj{M5El85KntN0fmQEUW0V(r=u z>J1n$cIfUEyEkRW6(kiU<)34xFtd^kE5Q94`YYJ6$h_Am5o*3Q#C@UH*!`WnPzhnz zSA3SgV#KHMzj*9J?-2uDc+{<#%e`sf0(am8Oy)i$7f>7g+XxTLJ^WX7p#K_F{QLaW zpYZ?PcXg=!>%;87O-uZDo1*frzfaEBD8VUP!9TNgz`sVd^=Gi>XR!HTUA6au|FdTN ztGGW+-uW!T^#%6-)oJk0zSyhm|B>E*W`F4JUscu~JMp$d-@f_mo4&~VKA-o!6Ysm^ zfXj&O5$;#8Z?*l{{4$(F7j!^Fu-^l_J;l|%nqLF&rV-tT4$7IncEz)O;eqc%t|k_1 z*>h_w)GNlv35w~lP8B15wf45QB>Z|z$SnODvpo+M z#19c3qJ`KXxfkhA`-qEB^$|Amciih|eD4=vsGO}$B`3tg!O^yvlapg-2SZq7bcs=2MJ>h8AA-NW6dcKtdl8#eP=hR?mAUAK-iyY}lbvBw~^#ONUd zhff~UYuv0!ohQtn-e&5u`QB5P&hwhMV0weW;|JF5-oJC57Hyi=Z`7oI)4HBDJG$4b zHqg1M%XkN8`^9kTTPoYz9<{Nn9AxKc7f0Q_!O6`z6FrbkZvMKXi}O{mX2O?>=UHAR zlsd@$`|x*5EsmOpxBT5|9^UE$i0_5{uT-I8x!O%yw-`2d&X31;UOaloSeT6MXTFPx z{fyXo4Kv=N!F>$4k6|90T%(x_8^OMkU=wjb!U82c*+((nmOUfUCDT?dc+1awAUpt^ z;z{m9WmQQx!ofc~y>2jPvry`K#MigrUu?Yv|IuRqpPMC4#XcPP3v|E>KKJr8F~b4oLHW@8 zyG)5NFuVRv7QO!mUNigWrlt0OChvJ$Vn4O^*VEHe)%k*d!5V)57u;uMvHjSfw)p%b znNzX`U;lEyaigxU-0%M)^jdPUiJe_3^r6LHBiF@@dky<6G|ZriWEM?yI{SO5T~8?p zR7~iC2=u_(!#m#h9zEzqy~Yg+oL!yc?d)y$p^L-^=)%3!6duaTMOyWd+!J5_J(&0$ z?3BTdI&yy=NK-J?h5HSX7C);sKll5xWxktPu~Ku>E3bFloj*O zo{GO*XuKDt#4&#{f*m9g?D&hM##1mK%j{1Ps~(7=_A8tnuEBco0TwIq>>x0*!+iU> z{U5py>iw*0jcPaWzpn5+H{pwnm9xOv^uQ0Pfd2pdQ!emdq6~h8U;R7I&lliddVf9X z{Ta}0$dGL-_8-WKDaiSdm>0&5Ic7Q0_Z2ERe{ddUk!aQCpQUF^aF6|$D|nBy;9jnA zFVO+7&d?)#KnX}+tz2NwtMvZG6lBu-v-6dA=PuXD{ha`FQ+UrM?sk^gQ|v7`lN!I@ zaF3sH{=Vg`yMX;hWxgxZ%ihspwr4Z1BfZCrFs?eZ_gOS`%0~9~#bzCVZ)RVxeic_@ zQR8wYOvJEE2}xPb=T@SB)|q`!`1tsOgpyVJw!9-oe&SK5W)^lkfO}a64*G$A$wgJh z2C213mitll5%=_G&g!?^mmN|A+&2Z&J-NrxJkNP$%a+*yzqpSa#F?_?%LXGFK8_(5 z5zF^Pmn&B`9Czjt|K|k%U(G}CGpF;r2V?)F2g(yWjEZwBU$bV7FFZZ#{HeNol|O=w z&-p*)~*>eID=nBeKO)$?DkJ-Ge7|D~fJ z4u_vE+81)7Xw!*ZAEvCB`?lBc0WTW2Z1Rv;UTX4U(E*3Cy%K{-9$sql-TB)q$lp#c z)}KVziT_{OzEbV>Jvxn;;j`$>*@P>FraO^JJot~yTt^ObGc^d?!9Dmd5bXOYargkS z)Q!c^`yIjlhbw7Il`Dz!6^(w4k_HcO*%Vex&f$$`lSX PAQ^O%8A$v$>C+JcIh& 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 z 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 J0`IH@{s&EnA87yp diff --git a/indra/newview/res/ll_icon.ico b/indra/newview/res/ll_icon.ico deleted file mode 100644 index c35a3fa3a3423e3ce6de9568c28978930ca99fe8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 364590 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
ZC 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 E51*^1{Qv*} diff --git a/indra/newview/res/singularity_icon.bmp b/indra/newview/res/singularity_icon.bmp deleted file mode 100644 index 7e27d0eca426515ee8730954a66e2874c0bb8b02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262198 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 BBW?fy diff --git a/indra/newview/res/singularity_icon_bc.ico b/indra/newview/res/singularity_icon_bc.ico deleted file mode 100644 index 1287f3e2096bd5f8df46fafca3ee055a80d1eddd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5430 zcma)=cTAS+8OC1_L~($)aR4rG@4ffLz4rze)`45Wz4xfvI2vuV#8GSNF-9ZWYK*a- zM%$dz6-|>iZU30#1k38_z0)gb_dB692pXa*gb>Ht71YtpVkeC=~O$@Ha z20@b`2x7JJIXxl>Ug|nU_w-26N5Xygm%n`aP^$l7dMdnq`}UEN=w{8D#WiZwsBV0G zd~&sF)!J38Sg}V`R8)V-KvsS3sr&6~*RGw?uwlb`DJdycTeWIcf&H8#;k*+6va+(m zCrp@7cI3#Bl_c?f`}S?#ty{OgI%A6PSLob5b?Vf)ARWG_S+nM^YSgIljI~y+TDQ3` z{r0zP*)p?3hYr(w^ytxl;J|?`IEV8VE?kHnMd=VG+=x}HR#iw(Pp>g|?%d=lQ>OG7 zH*Vb65hF&V4Hz(BL&uIC&&i%In>1^nVA{Ca^=dDrAwD~TCiZj zfH`yKj2Sd&&^p=uIw>hBFEKIE8Z>BNto~Z|T5@u-)1yU;7S_6TYm*Mvy?ggJ(%}dC zYp{O(`s&zLl>OkJIddjbYqTU*&(_?yabp5{$et4k2?-zS)vITS;_5`cx4L!fx_kQG z>D0J!V*}soApe{EZ}Q!>@#DwG72|jB3j80Qlao^hT@?Qq*?G%jD#li|YE_Gii*qZV zt5>gXin%G~R=otE*s)_1JXlGqo{x}ke*w1g%HhSv z##-gdl}-8WgzG9*s+e-!Jwxoh4&;R5h=*JZ$N}+? zsUA(7U^8aS`03~P;*;Na-xGh%7&U5?O`bg24jw#cSFc`mtGL45C!RXTRI7IS^l7_w z>z3WPal`qDTBHu(0&<{h*RC!HJ-&SSK~l@>pl7wEv4QV#v3cLVeYS4hI_uS|m&-3~ zK!5R~vxU4SzQo$|ApFExMb$tEzRjOMKRpD05od^VU5j0rfyW zk|Wdzxk22(M&M?T@8d5XD)7H})zZ?^ay-6x>WAV+n>TNs9Y22DmM>rKgnZ>(@YLU& zP-6yR)(Lz<~qbp@VXG3wfNKo$X@Dxts~N z;9oE31Mh*4&%yS60Gr_+U;Eha`LAEUel~pgaF;jgg?9e@d3*i(_0PQhs^1^u7ve_z zumygB2RzrK3)kEO7mnf?YK1-G4!0lMeci!7o_nZ$>hsZ~M|S`IeY<({=I_j`3_9q! zYwTe!x$AYnHm{5CeIC<)hp$4heg7lx@IAJx2iv`S_w4!e=dO<7FZH^2!gsTPp4~;i z!e?-|*MYOa$3`!F$34D7PkfHw&>d`eRmN}ENu?-x!5%Ip}J9Hu5@Bn+^tF2qNI*xq$^r>CCbjjs2xlO*FJ9o|w zA3khr)~ta`k9vIhsZy6NUH(J9P&3#;Jn#kQqYGGwdZ=yQyxH}dqeqXrn!!GNOI$TS z*sfi>jCzJ2XU?2yM~)n^3l}b!<|`Kq_(d@o?D1JuBUj-`{NQ7O9$Zty_?Noey?eLa zzI~hc*|lreoL^6!I_3C^x+c%4H?WT#J7#Ckp0%}W*P7-z*PAq7q2FJngC_ODKl!jD z+)Le(xA+2V{6SuD4}K7DIBd9CGaCKO^>*qTp2Rl##m=2O;aF3zbG%0XqEFMK_5IpH zX-+h5AZzu&ZJ)1Os)&s6U@%I|fWAKI1nJf2ezGJ79omRrUwJIwNe zAnT*(@Y^6rcpn5YlBl5pNbX6MR^U9$3MsAj(X>L-6tKl?y0%YSG3D8BoXcx9`2s|oMS+8@Rl?3a{NoU5uA z{77>^9&{M%m?Vge%^V(rtjzG=FSyHD!TFvILuS{ zl52!>>G9YNEyg#VNxxR>h>5rPbIuQfK z^b2x>_|V(g$MfPS$6Ih4zGXh*Jl=2xmvmxGE&Tj702%y}a3mZIKjSlc z5A&}0Al1j8Rqr$l)I0L%m&BL%NbIH85L0*+T;(0Pu`_h9zm{JoO5VZ$_!vB};YIWo z@BdqS3-ZqI3th$CapKc8;?PIpw7-eB{~^q0`W<1L^0;p3vs_zN5WJ5Gg11pYkgKU3 S$(C1&6TW{dVb4eX^7CJ4U0M?W diff --git a/indra/newview/res/snowglobe_icon.BMP b/indra/newview/res/snowglobe_icon.BMP deleted file mode 100644 index 778779b1fd7a2733a290b05fe49d1ec031a6d0b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262198 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 DP*RSk diff --git a/indra/newview/res/snowglobe_icon.png b/indra/newview/res/snowglobe_icon.png deleted file mode 100644 index fdd54c3aa0bfe3d9155817b537171dae893134c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46123 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 Date: Fri, 19 Dec 2014 19:25:31 -0600 Subject: [PATCH 35/55] Fixed doublequote issue with --extra_libraries --- indra/newview/CMakeLists.txt | 12 ++++++------ indra/newview/viewer_manifest.py | 10 ++++++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c9b21e390..aa679e99f 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1450,9 +1450,9 @@ if(FMOD_LIBRARY_RELEASE) #viewer_manifest.py needs these libraries (for now its all dynlib) if(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|optimized ${fmod_lib_rel_name}|debug ${fmod_lib_deb_name}") + set(MANIFEST_LIBRARIES ${MANIFEST_LIBRARIES}|optimized ${fmod_lib_rel_name}|debug ${fmod_lib_deb_name}) else(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "--extra_libraries=optimized ${fmod_lib_rel_name}|debug ${fmod_lib_deb_name}") + set(MANIFEST_LIBRARIES optimized ${fmod_lib_rel_name}|debug ${fmod_lib_deb_name}) endif(MANIFEST_LIBRARIES) if(WINDOWS) #If windows, fmod_lib_ points to a dll. The correct .lib needs to be linked (but copying is not necessary) @@ -1530,7 +1530,7 @@ if (WINDOWS) --login_channel=${VIEWER_LOGIN_CHANNEL} --source=${CMAKE_CURRENT_SOURCE_DIR} --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/touched.bat - ${MANIFEST_LIBRARIES} + --extra_libraries="${MANIFEST_LIBRARIES}" DEPENDS ${VIEWER_BINARY_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py @@ -1553,7 +1553,7 @@ if (WINDOWS) --login_channel=${VIEWER_LOGIN_CHANNEL} --source=${CMAKE_CURRENT_SOURCE_DIR} --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/copy_touched.bat - ${MANIFEST_LIBRARIES} + --extra_libraries="${MANIFEST_LIBRARIES}" DEPENDS ${VIEWER_BINARY_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py @@ -1652,7 +1652,7 @@ if (LINUX) --source=${CMAKE_CURRENT_SOURCE_DIR} --standalone=${STANDALONE} --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/.${product}.touched - ${MANIFEST_LIBRARIES} + --extra_libraries="${MANIFEST_LIBRARIES}" DEPENDS secondlife-stripped ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py @@ -1676,7 +1676,7 @@ if (LINUX) --login_channel=${VIEWER_LOGIN_CHANNEL} --source=${CMAKE_CURRENT_SOURCE_DIR} --standalone=${STANDALONE} - ${MANIFEST_LIBRARIES} + --extra_libraries="${MANIFEST_LIBRARIES}" DEPENDS secondlife-stripped ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 6e9466449..71f6068d5 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -197,9 +197,15 @@ class ViewerManifest(LLManifest): def add_extra_libraries(self): found_libs = [] if 'extra_libraries' in self.args: - path_list = self.args['extra_libraries'].split('|') + try: + path_list = self.args['extra_libraries'].strip('"').split('|') + except: + return None for cur_path in path_list: - config, file = cur_path.split(' ', 1) + try: + config, file = cur_path.split(' ', 1) + except: + config, file = (None, None) if(config == 'optimized'): if(self.args['configuration'].lower() != 'release' and self.args['configuration'].lower() != 'relwithdebinfo'): continue From e0fb73414b35a63cc27871661fd64dd989be7b1d Mon Sep 17 00:00:00 2001 From: Shyotl Date: Mon, 22 Dec 2014 02:08:18 -0600 Subject: [PATCH 36/55] Mac fixup. --- indra/cmake/CARes.cmake | 2 +- indra/cmake/Linking.cmake | 36 +++++++++++----------- indra/develop.py | 6 ++-- indra/newview/viewer_manifest.py | 1 + indra/plugins/example_basic/CMakeLists.txt | 2 +- indra/plugins/example_media/CMakeLists.txt | 2 +- indra/plugins/filepicker/CMakeLists.txt | 2 +- indra/plugins/quicktime/CMakeLists.txt | 2 +- indra/plugins/webkit/CMakeLists.txt | 6 ++-- 9 files changed, 30 insertions(+), 29 deletions(-) diff --git a/indra/cmake/CARes.cmake b/indra/cmake/CARes.cmake index b08f051c8..062b283c1 100644 --- a/indra/cmake/CARes.cmake +++ b/indra/cmake/CARes.cmake @@ -13,7 +13,7 @@ else (STANDALONE) add_definitions("-DCARES_STATICLIB") set(CARES_LIBRARIES areslib) elseif (DARWIN) - set(APR_LIBRARIES + set(CARES_LIBRARIES debug libcares.a optimized libcares.a ) diff --git a/indra/cmake/Linking.cmake b/indra/cmake/Linking.cmake index 36f087545..a0ce0f6f1 100644 --- a/indra/cmake/Linking.cmake +++ b/indra/cmake/Linking.cmake @@ -5,35 +5,35 @@ set(${CMAKE_CURRENT_LIST_FILE}_INCLUDED "YES") include(Variables) if (NOT STANDALONE) - set(ARCH_PREBUILT_DIRS - ${LIBS_PREBUILT_DIR}/lib - ${LIBS_PREBUILT_LEGACY_DIR}/lib - ) - set(ARCH_PREBUILT_DIRS_RELEASE - ${LIBS_PREBUILT_DIR}/lib/release - ${LIBS_PREBUILT_LEGACY_DIR}/lib/release - ) - set(ARCH_PREBUILT_DIRS_DEBUG - ${LIBS_PREBUILT_DIR}/lib/debug - ${LIBS_PREBUILT_LEGACY_DIR}/lib/debug - ) + + if(CMAKE_BUILD_TYPE) + string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_LOWER) + endif(CMAKE_BUILD_TYPE) if(WINDOWS OR ${CMAKE_GENERATOR} MATCHES "Xcode") # the cmake xcode and VS generators implicitly append ${CMAKE_CFG_INTDIR} to the library paths for us # fortunately both windows and darwin are case insensitive filesystems so this works. - set(ARCH_PREBUILT_LINK_DIRS "${ARCH_PREBUILT_DIRS}") + set(ARCH_PREBUILT_LINK_DIRS + ${LIBS_PREBUILT_DIR}/lib + ${LIBS_PREBUILT_LEGACY_DIR}/lib + ) else(WINDOWS OR ${CMAKE_GENERATOR} MATCHES "Xcode") # else block is for linux and any other makefile based generators - string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_LOWER) - set(ARCH_PREBUILT_LINK_DIRS ${ARCH_PREBUILT_DIRS}/${CMAKE_BUILD_TYPE_LOWER}) + set(ARCH_PREBUILT_LINK_DIRS + ${LIBS_PREBUILT_DIR}/lib/${CMAKE_BUILD_TYPE_LOWER} + ${LIBS_PREBUILT_LEGACY_DIR}/lib/${CMAKE_BUILD_TYPE_LOWER} + ) endif(WINDOWS OR ${CMAKE_GENERATOR} MATCHES "Xcode") - if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Release") + if (NOT "${CMAKE_BUILD_TYPE_LOWER}" STREQUAL "release") # When we're building something other than Release, append the # packages/lib/release directory to deal with autobuild packages that don't # provide (e.g.) lib/debug libraries. - list(APPEND ARCH_PREBUILT_LINK_DIRS ${ARCH_PREBUILT_DIRS_RELEASE}) - endif (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Release") + list(APPEND ARCH_PREBUILT_LINK_DIRS + ${LIBS_PREBUILT_DIR}/lib/release + ${LIBS_PREBUILT_LEGACY_DIR}/lib/release + ) + endif (NOT "${CMAKE_BUILD_TYPE_LOWER}" STREQUAL "release") endif (NOT STANDALONE) link_directories(${ARCH_PREBUILT_LINK_DIRS}) diff --git a/indra/develop.py b/indra/develop.py index 8e1dc4150..9eee9aefe 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -112,13 +112,13 @@ class PlatformSetup(object): def build_dirs(self): '''Return the top-level directories in which builds occur. + This can return more than one directory, e.g. if doing a + 32-bit viewer and server build on Linux.''' + if(os.path.basename(os.path.normpath(os.getcwd())) == 'indra'): prefix = '../' else: prefix = '' - - This can return more than one directory, e.g. if doing a - 32-bit viewer and server build on Linux.''' return [prefix+'build-' + self.platform()] diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 71f6068d5..61197aa4e 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -37,6 +37,7 @@ import re import tarfile import time import subprocess +import errno viewer_dir = os.path.dirname(__file__) # add llmanifest library to our path so we don't have to muck with PYTHONPATH sys.path.append(os.path.join(viewer_dir, '../lib/python/indra/util')) diff --git a/indra/plugins/example_basic/CMakeLists.txt b/indra/plugins/example_basic/CMakeLists.txt index e97cd3243..616bda770 100644 --- a/indra/plugins/example_basic/CMakeLists.txt +++ b/indra/plugins/example_basic/CMakeLists.txt @@ -62,7 +62,7 @@ if (DARWIN) PREFIX "" BUILD_WITH_INSTALL_RPATH 1 INSTALL_NAME_DIR "@executable_path" - LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_basic/basic_plugin_base.exp" + LINK_FLAGS "-exported_symbols_list '${CMAKE_CURRENT_SOURCE_DIR}/../base_basic/basic_plugin_base.exp'" ) endif (DARWIN) diff --git a/indra/plugins/example_media/CMakeLists.txt b/indra/plugins/example_media/CMakeLists.txt index eefdd5251..7f9def1c9 100644 --- a/indra/plugins/example_media/CMakeLists.txt +++ b/indra/plugins/example_media/CMakeLists.txt @@ -75,7 +75,7 @@ if (DARWIN) PREFIX "" BUILD_WITH_INSTALL_RPATH 1 INSTALL_NAME_DIR "@executable_path" - LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_media/media_plugin_base.exp" + LINK_FLAGS "-exported_symbols_list '${CMAKE_CURRENT_SOURCE_DIR}/../base_media/media_plugin_base.exp'" ) endif (DARWIN) diff --git a/indra/plugins/filepicker/CMakeLists.txt b/indra/plugins/filepicker/CMakeLists.txt index c973954dd..ec896b69c 100644 --- a/indra/plugins/filepicker/CMakeLists.txt +++ b/indra/plugins/filepicker/CMakeLists.txt @@ -102,7 +102,7 @@ if (DARWIN) PREFIX "" BUILD_WITH_INSTALL_RPATH 1 INSTALL_NAME_DIR "@executable_path" - LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_basic/basic_plugin_base.exp" + LINK_FLAGS "-exported_symbols_list '${CMAKE_CURRENT_SOURCE_DIR}/../base_basic/basic_plugin_base.exp'" ) endif (DARWIN) diff --git a/indra/plugins/quicktime/CMakeLists.txt b/indra/plugins/quicktime/CMakeLists.txt index c827404c2..d1b17331c 100644 --- a/indra/plugins/quicktime/CMakeLists.txt +++ b/indra/plugins/quicktime/CMakeLists.txt @@ -76,7 +76,7 @@ if (QUICKTIME) PREFIX "" BUILD_WITH_INSTALL_RPATH 1 INSTALL_NAME_DIR "@executable_path" - LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_media/media_plugin_base.exp" + LINK_FLAGS "-exported_symbols_list '${CMAKE_CURRENT_SOURCE_DIR}/../base_media/media_plugin_base.exp'" ) # We use a bunch of deprecated system APIs. diff --git a/indra/plugins/webkit/CMakeLists.txt b/indra/plugins/webkit/CMakeLists.txt index 615333227..f135c8f78 100644 --- a/indra/plugins/webkit/CMakeLists.txt +++ b/indra/plugins/webkit/CMakeLists.txt @@ -117,15 +117,15 @@ if (DARWIN) PREFIX "" BUILD_WITH_INSTALL_RPATH 1 INSTALL_NAME_DIR "@executable_path" - LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_media/media_plugin_base.exp" + LINK_FLAGS "-exported_symbols_list '${CMAKE_CURRENT_SOURCE_DIR}/../base_media/media_plugin_base.exp'" ) # copy the webkit dylib to the build directory add_custom_command( TARGET media_plugin_webkit POST_BUILD # OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/libllqtwebkit.dylib - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib/release/libllqtwebkit.dylib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ - DEPENDS media_plugin_webkit ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib/release/libllqtwebkit.dylib + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/packages/libraries/universal-darwin/lib/release/libllqtwebkit.dylib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + DEPENDS media_plugin_webkit ${CMAKE_BINARY_DIR}/packages/libraries/universal-darwin/lib/release/libllqtwebkit.dylib ) endif (DARWIN) From 28024d7a01c42ca8e33b1f4770542641aa5a827b Mon Sep 17 00:00:00 2001 From: Shyotl Date: Mon, 22 Dec 2014 02:42:31 -0600 Subject: [PATCH 37/55] Try better handling CMAKE_BUILD_TYPE if xcode (supports multi-configurations, unlike Unix Makefiles) --- indra/develop.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/indra/develop.py b/indra/develop.py index 9eee9aefe..462263ea0 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -418,15 +418,17 @@ class DarwinSetup(UnixSetup): word_size=self.word_size, unattended=self.unattended, project_name=self.project_name, - universal=self.universal, - type=self.build_type.upper(), + universal='', + type='', ) + if(self.generator != 'Xcode'): + args['type'] = '-DCMAKE_BUILD_TYPE=%s' % self.build_type.upper() if self.universal == 'ON': args['universal'] = '-DCMAKE_OSX_ARCHITECTURES:STRING=\'i386;ppc\'' #if simple: # return 'cmake %(opts)s %(dir)r' % args return ('cmake -G %(generator)r ' - '-DCMAKE_BUILD_TYPE:STRING=%(type)s ' + '%(type) ' '-DSTANDALONE:BOOL=%(standalone)s ' '-DUNATTENDED:BOOL=%(unattended)s ' '-DWORD_SIZE:STRING=%(word_size)s ' From 0ac3fd05631c612b651ba9010ea63ffaa115bd43 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Mon, 22 Dec 2014 20:52:11 -0600 Subject: [PATCH 38/55] Rename linux build directory from viewer-* to build-*. Also have mac use the unix run_build if not using Xcode --- indra/develop.py | 29 ++++++++++++++++++++--------- indra/newview/viewer_manifest.py | 11 ++++++----- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/indra/develop.py b/indra/develop.py index 462263ea0..a5d1dd9d0 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -280,14 +280,7 @@ class LinuxSetup(UnixSetup): return 'linux' def build_dirs(self): - if(os.path.basename(os.path.normpath(os.getcwd())) == 'indra'): - prefix = '../' - else: - prefix = '' - - platform_build = '%s-%s' % (self.platform(), self.build_type.lower()) - - return [prefix+'viewer-' + platform_build] + return [PlatformSetup.build_dirs(self)[0]+'-'+self.build_type.lower()] def cmake_commandline(self, src_dir, build_dir, opts, simple): args = dict( @@ -409,6 +402,12 @@ class DarwinSetup(UnixSetup): else: return UnixSetup.arch(self) + def build_dirs(self): + if(self.generator == 'Xcode'): + return PlatformSetup.build_dirs(self) + else: + return [PlatformSetup.build_dirs(self)[0]+'-'+self.build_type.lower()] + def cmake_commandline(self, src_dir, build_dir, opts, simple): args = dict( dir=src_dir, @@ -428,7 +427,7 @@ class DarwinSetup(UnixSetup): #if simple: # return 'cmake %(opts)s %(dir)r' % args return ('cmake -G %(generator)r ' - '%(type) ' + '%(type)s ' '-DSTANDALONE:BOOL=%(standalone)s ' '-DUNATTENDED:BOOL=%(unattended)s ' '-DWORD_SIZE:STRING=%(word_size)s ' @@ -437,6 +436,18 @@ class DarwinSetup(UnixSetup): '%(opts)s %(dir)r' % args) def run_build(self, opts, targets): + if(self.generator != 'Xcode'): + if targets: + targets = ' '.join(targets) + else: + targets = 'all' + + for d in self.build_dirs(): + cmd = 'make -C %r %s %s' % (d, ' '.join(opts), targets) + print 'Running %r' % cmd + self.run(cmd) + return + cwd = getcwd() if targets: targets = ' '.join(['-target ' + repr(t) for t in targets]) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 61197aa4e..2c053c312 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -687,12 +687,13 @@ class DarwinManifest(ViewerManifest): if not os.path.exists (self.src_path_of(dmg_template)): dmg_template = os.path.join ('installers', 'darwin', 'release-dmg') - for s,d in {self.get_dst_prefix():app_name + ".app", - os.path.join(dmg_template, "_VolumeIcon.icns"): ".VolumeIcon.icns", - os.path.join(dmg_template, "background.jpg"): "background.jpg", - os.path.join(dmg_template, "_DS_Store"): ".DS_Store"}.items(): + + for s,d in {self.build_path_of(self.get_dst_prefix()):app_name + ".app", + self.src_path_of(os.path.join(dmg_template, "_VolumeIcon.icns")): ".VolumeIcon.icns", + self.src_path_of(os.path.join(dmg_template, "background.jpg")): "background.jpg", + self.src_path_of(os.path.join(dmg_template, "_DS_Store")): ".DS_Store"}.items(): print "Copying to dmg", s, d - self.copy_action(self.src_path_of(s), os.path.join(volpath, d)) + self.copy_action(s, os.path.join(volpath, d)) # Hide the background image, DS_Store file, and volume icon file (set their "visible" bit) self.run_command('SetFile -a V "' + os.path.join(volpath, ".VolumeIcon.icns") + '"') From ecd58e392469c1fb7df504114e54a8f89aa10b7e Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 23 Dec 2014 20:00:01 -0600 Subject: [PATCH 39/55] Reduce some diagnostic spam. --- indra/lib/python/indra/util/llmanifest.py | 1 - indra/newview/viewer_manifest.py | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py index 8b8ed825d..0059faa2e 100644 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -642,7 +642,6 @@ class LLManifest(object): os.path.normpath(os.path.join(self.get_alt_build_prefix(), src))] ) for path in paths: - print path if self.wildcard_pattern.search(path): is_glob = True for s,d in self.expand_globs(path, dst): diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 2c053c312..fa9a8cdb5 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -197,12 +197,14 @@ class ViewerManifest(LLManifest): def add_extra_libraries(self): found_libs = [] - if 'extra_libraries' in self.args: + if 'extra_libraries' in self.args and self.args['extra_libraries'] != '': try: path_list = self.args['extra_libraries'].strip('"').split('|') except: return None for cur_path in path_list: + if cur_path is None or cur_path == '': + continue try: config, file = cur_path.split(' ', 1) except: @@ -215,7 +217,7 @@ class ViewerManifest(LLManifest): if(self.args['configuration'].lower() != 'debug'): continue cur_path = file - if(cur_path != None): + if(cur_path != ''): found_libs += self.path_optional(cur_path) return found_libs From 00123d2ddedac053dec584ea0d2e1e5b8cf3a5c5 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 25 Dec 2014 01:57:59 -0600 Subject: [PATCH 40/55] Not sure why this line got deleted. It's needed for linux breakpad. --- indra/newview/viewer_manifest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index fa9a8cdb5..e33bbf6fa 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -752,6 +752,8 @@ class LinuxManifest(ViewerManifest): # self.path("secondlife-stripped","bin/"+self.binary_name()) #else: # self.path("secondlife-bin","bin/"+self.binary_name()) + self.path("secondlife-bin","bin/"+self.binary_name()) + if self.prefix(src="", dst="bin"): self.path2basename("../llplugin/slplugin", "SLPlugin") self.end_prefix("bin") From b3bd91877c4b62aa718a225f6d393038ba0c0c29 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Fri, 26 Dec 2014 23:51:05 -0600 Subject: [PATCH 41/55] Fixed CMP0048 properly. --- indra/CMakeLists.txt | 2 -- indra/cmake/BuildVersion.cmake | 17 ++++++++--------- indra/cmake/UnixInstall.cmake | 2 +- indra/newview/CMakeLists.txt | 8 ++++---- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 7ed2a7d82..bbf77c644 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -17,7 +17,6 @@ cmake_minimum_required(VERSION 2.6.2 FATAL_ERROR) # can be removed when we use full paths for all libraries. cmake_policy(SET CMP0003 OLD) if(NOT (CMAKE_MAJOR_VERSION LESS 3)) -cmake_policy(SET CMP0048 OLD) cmake_policy(SET CMP0026 OLD) endif(NOT (CMAKE_MAJOR_VERSION LESS 3)) @@ -31,7 +30,6 @@ include(Variables) # Load versions now. Install locations need them. include(BuildVersion) - include(UnixInstall) if (NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) diff --git a/indra/cmake/BuildVersion.cmake b/indra/cmake/BuildVersion.cmake index 54ae4b152..457a1bb16 100644 --- a/indra/cmake/BuildVersion.cmake +++ b/indra/cmake/BuildVersion.cmake @@ -70,13 +70,12 @@ if (LINUX) ) endif (LINUX) -# Compose the version. -set(viewer_VERSION "${vMAJOR}.${vMINOR}.${vPATCH}.${vBUILD}") -if (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$") - message(STATUS "Version is ${viewer_VERSION}") -else (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$") - message(FATAL_ERROR "Could not determine version (${viewer_VERSION})") -endif (viewer_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$") -# Report version to caller. -#set(viewer_VERSION "${viewer_VERSION}" PARENT_SCOPE) +# Compose the version. +set(${ROOT_PROJECT_NAME}_VERSION "${vMAJOR}.${vMINOR}.${vPATCH}.${vBUILD}") +if (${ROOT_PROJECT_NAME}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$") + message(STATUS "Version is ${${ROOT_PROJECT_NAME}_VERSION}") +else (${ROOT_PROJECT_NAME}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$") + message(FATAL_ERROR "Could not determine version (${${ROOT_PROJECT_NAME}_VERSION})") +endif (${ROOT_PROJECT_NAME}_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$") + diff --git a/indra/cmake/UnixInstall.cmake b/indra/cmake/UnixInstall.cmake index 630999d80..f566aad3d 100644 --- a/indra/cmake/UnixInstall.cmake +++ b/indra/cmake/UnixInstall.cmake @@ -11,6 +11,6 @@ if (INSTALL) set(APP_BIN_DIR bin) endif(NOT APP_BIN_DIR) if(NOT APP_SHARE_DIR) - set(APP_SHARE_DIR share/secondlife-${viewer_VERSION}) + set(APP_SHARE_DIR share/secondlife-${${ROOT_PROJECT_NAME}_VERSION}) endif(NOT APP_SHARE_DIR) endif (INSTALL) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index aa679e99f..e49889db3 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1632,7 +1632,7 @@ if (LINUX) DEPENDS ${VIEWER_BINARY_NAME} ) - set(product ${VIEWER_BRANDING_NAME_CAMELCASE}-${ARCH}-${viewer_VERSION}) + set(product ${VIEWER_BRANDING_NAME_CAMELCASE}-${ARCH}-${${ROOT_PROJECT_NAME}_VERSION}) add_custom_command( OUTPUT ${product}.tar.bz2 @@ -1700,10 +1700,10 @@ if (DARWIN) MACOSX_BUNDLE_INFO_STRING "A stable third-party Second Life viewer." MACOSX_BUNDLE_ICON_FILE "${VIEWER_BRANDING_ID}_icon.icns" MACOSX_BUNDLE_GUI_IDENTIFIER "${VIEWER_BRANDING_NAME}" - MACOSX_BUNDLE_LONG_VERSION_STRING "${viewer_VERSION}" + MACOSX_BUNDLE_LONG_VERSION_STRING "${${ROOT_PROJECT_NAME}_VERSION}" MACOSX_BUNDLE_BUNDLE_NAME "${VIEWER_BRANDING_NAME}" - MACOSX_BUNDLE_SHORT_VERSION_STRING "${viewer_VERSION}" - MACOSX_BUNDLE_BUNDLE_VERSION "${viewer_VERSION}" + MACOSX_BUNDLE_SHORT_VERSION_STRING "${${ROOT_PROJECT_NAME}_VERSION}" + MACOSX_BUNDLE_BUNDLE_VERSION "${${ROOT_PROJECT_NAME}_VERSION}" MACOSX_BUNDLE_COPYRIGHT "Copyright 2013 Siana Gearz" ) From f5d3bc2b7b1bf8d15bc426a80cf72b07c7e3c18b Mon Sep 17 00:00:00 2001 From: Shyotl Date: Fri, 26 Dec 2014 23:53:14 -0600 Subject: [PATCH 42/55] Fixed CMP0026 properly. Requires cmake 2.8.8+ --- indra/CMakeLists.txt | 7 ++----- indra/cmake/CopyBackToSource.cmake | 2 +- indra/cmake/LLAddBuildTest.cmake | 2 +- indra/cmake/LLSharedLibs.cmake | 5 +++-- indra/newview/CMakeLists.txt | 12 ++++++------ indra/test/CMakeLists.txt | 2 +- indra/test_apps/llplugintest/CMakeLists.txt | 10 +++++----- 7 files changed, 19 insertions(+), 21 deletions(-) diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index bbf77c644..d213b0efd 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -4,7 +4,7 @@ # other commands to guarantee full compatibility # with the version specified -cmake_minimum_required(VERSION 2.6.2 FATAL_ERROR) +cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR) # Eventually the third-party support modules (cmake/*.cmake) should # know the full path to all libraries. Until that happens we need @@ -15,10 +15,7 @@ cmake_minimum_required(VERSION 2.6.2 FATAL_ERROR) # CMP0003 to OLD and link to one library (apr) on a per-configuration # basis to convince CMake to add the proper link directory. This line # can be removed when we use full paths for all libraries. -cmake_policy(SET CMP0003 OLD) -if(NOT (CMAKE_MAJOR_VERSION LESS 3)) -cmake_policy(SET CMP0026 OLD) -endif(NOT (CMAKE_MAJOR_VERSION LESS 3)) +#cmake_policy(SET CMP0003 OLD) set(ROOT_PROJECT_NAME "Singularity" CACHE STRING "The root project/makefile/solution name. Defaults to Singularity.") diff --git a/indra/cmake/CopyBackToSource.cmake b/indra/cmake/CopyBackToSource.cmake index d217df9ae..d09a216e8 100644 --- a/indra/cmake/CopyBackToSource.cmake +++ b/indra/cmake/CopyBackToSource.cmake @@ -2,7 +2,7 @@ # Copies a binary back to the source directory MACRO(COPY_BACK_TO_SOURCE target) - GET_TARGET_PROPERTY(FROM ${target} LOCATION) + SET(FROM $) SET(TO ${CMAKE_CURRENT_SOURCE_DIR}) #MESSAGE("TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${FROM} ${TO}") ADD_CUSTOM_COMMAND( diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index 3d42088bc..290db9f22 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -87,7 +87,7 @@ MACRO(ADD_BUILD_TEST_INTERNAL name parent libraries source_files) ${libraries} ) - GET_TARGET_PROPERTY(TEST_EXE ${name}_test LOCATION) + SET(TEST_EXE $) SET(TEST_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${name}_test_ok.txt) IF ("${wrapper}" STREQUAL "") diff --git a/indra/cmake/LLSharedLibs.cmake b/indra/cmake/LLSharedLibs.cmake index 14dd67f32..8d31d9e77 100644 --- a/indra/cmake/LLSharedLibs.cmake +++ b/indra/cmake/LLSharedLibs.cmake @@ -1,7 +1,7 @@ # ll_deploy_sharedlibs_command # target_exe: the cmake target of the executable for which the shared libs will be deployed. macro(ll_deploy_sharedlibs_command target_exe) - get_target_property(TARGET_LOCATION ${target_exe} LOCATION) + SET(TARGET_LOCATION $) get_filename_component(OUTPUT_PATH ${TARGET_LOCATION} PATH) if(DARWIN) @@ -42,7 +42,8 @@ macro(ll_stage_sharedlib DSO_TARGET) # Also this directory is shared with RunBuildTest.cmake, y'know, for the tests. set_target_properties(${DSO_TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${SHARED_LIB_STAGING_DIR}) if(NOT WINDOWS) - get_target_property(DSO_PATH ${DSO_TARGET} LOCATION) + + SET(DSO_PATH $) get_filename_component(DSO_FILE ${DSO_PATH} NAME) if(DARWIN) set(SHARED_LIB_STAGING_DIR_CONFIG ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/Resources) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index e49889db3..24138f407 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1846,7 +1846,7 @@ if (WINDOWS) ENDFOREACH(RUNTIME_LIB ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}) ENDIF(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS) - get_target_property(BUILT_LLCOMMON llcommon LOCATION) + SET(BUILT_LLCOMMON $) set_target_properties(llcommon PROPERTIES @@ -1867,7 +1867,7 @@ if (WINDOWS) COMMENT "Copying llcommon.dll to the runtime folder." ) - get_target_property(BUILT_SLPLUGIN SLPlugin LOCATION) + SET(BUILT_SLPLUGIN $) add_custom_command( TARGET ${VIEWER_BINARY_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} @@ -1879,7 +1879,7 @@ if (WINDOWS) COMMENT "Copying SLPlugin executable to the runtime folder." ) - get_target_property(BUILT_WEBKIT_PLUGIN media_plugin_webkit LOCATION) + SET(BUILT_WEBKIT_PLUGIN $) add_custom_command( TARGET ${VIEWER_BINARY_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} @@ -1891,7 +1891,7 @@ if (WINDOWS) COMMENT "Copying WebKit Plugin to the runtime folder." ) - get_target_property(BUILT_QUICKTIME_PLUGIN media_plugin_quicktime LOCATION) + SET(BUILT_QUICKTIME_PLUGIN $) add_custom_command( TARGET ${VIEWER_BINARY_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} @@ -1903,7 +1903,7 @@ if (WINDOWS) COMMENT "Copying Quicktime Plugin to the runtime folder." ) - get_target_property(BUILT_FILEPICKER_PLUGIN basic_plugin_filepicker LOCATION) + SET(BUILT_FILEPICKER_PLUGIN $) add_custom_command( TARGET ${VIEWER_BINARY_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} @@ -1917,7 +1917,7 @@ if (WINDOWS) # winmm doesn't build on windows 64 if(WORD_SIZE EQUAL 32) - get_target_property(BUILT_WINMM_SHIM_PLUGIN winmm_shim LOCATION) + SET(BUILT_WINMM_SHIM_PLUGIN $) add_custom_command( TARGET ${VIEWER_BINARY_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index de440d2df..694121b87 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -129,7 +129,7 @@ if (WINDOWS) ) endif (WINDOWS) -get_target_property(TEST_EXE test LOCATION) +SET(TEST_EXE $) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/cpp_tests_ok.txt diff --git a/indra/test_apps/llplugintest/CMakeLists.txt b/indra/test_apps/llplugintest/CMakeLists.txt index f226c139e..b3f1f72ad 100644 --- a/indra/test_apps/llplugintest/CMakeLists.txt +++ b/indra/test_apps/llplugintest/CMakeLists.txt @@ -330,33 +330,33 @@ else (DARWIN) ) endif (DARWIN) -get_target_property(BUILT_SLPLUGIN SLPlugin LOCATION) +SET(BUILT_SLPLUGIN $) add_custom_command(TARGET llmediaplugintest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_SLPLUGIN} ${PLUGINS_DESTINATION_DIR} DEPENDS ${BUILT_SLPLUGIN} ) if (DARWIN OR WINDOWS) - get_target_property(BUILT_WEBKIT_PLUGIN media_plugin_webkit LOCATION) + SET(BUILT_WEBKIT_PLUGIN $) add_custom_command(TARGET llmediaplugintest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_WEBKIT_PLUGIN} ${PLUGINS_DESTINATION_DIR} DEPENDS ${BUILT_WEBKIT_PLUGIN} ) - get_target_property(BUILT_QUICKTIME_PLUGIN media_plugin_quicktime LOCATION) + SET(BUILT_QUICKTIME_PLUGIN $) add_custom_command(TARGET llmediaplugintest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_QUICKTIME_PLUGIN} ${PLUGINS_DESTINATION_DIR} DEPENDS ${BUILT_QUICKTIME_PLUGIN} ) - get_target_property(BUILT_EXAMPLE_PLUGIN media_plugin_example LOCATION) + SET(BUILT_EXAMPLE_PLUGIN $) add_custom_command(TARGET llmediaplugintest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_EXAMPLE_PLUGIN} ${PLUGINS_DESTINATION_DIR} DEPENDS ${BUILT_EXAMPLE_PLUGIN} ) # copy over bookmarks file if llmediaplugintest gets built - get_target_property(BUILT_LLMEDIAPLUGINTEST llmediaplugintest LOCATION) + SET(BUILT_LLMEDIAPLUGINTEST $) add_custom_command(TARGET llmediaplugintest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bookmarks.txt ${CMAKE_CURRENT_BINARY_DIR}/ DEPENDS ${BUILT_LLMEDIAPLUGINTEST} From 86240966b6d17ba1bc6b6be1c9192fa2c80eb41a Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sat, 27 Dec 2014 02:54:04 -0600 Subject: [PATCH 43/55] Include build version and arch/platform in generated symbol file's name. --- indra/newview/CMakeLists.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 24138f407..dcf99a751 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1755,11 +1755,13 @@ if (INSTALL) include(${CMAKE_CURRENT_SOURCE_DIR}/ViewerInstall.cmake) endif (INSTALL) + if (PACKAGE) + set(SYMBOL_NAME ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${VIEWER_BRANDING_NAME_CAMELCASE}-${${ROOT_PROJECT_NAME}_VERSION}-symbols) set(SYMBOL_SEARCH_DIRS "") if (WINDOWS) list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}") - set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-windows.tar.bz2") + set(VIEWER_SYMBOL_FILE "${SYMBOL_NAME}-${PREBUILT_TYPE}.tar.bz2") # slplugin.exe failing symbols dump - need to debug, might have to do with updated version of google breakpad # set(VIEWER_EXE_GLOBS "${VIEWER_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX} slplugin.exe") set(VIEWER_EXE_GLOBS "${VIEWER_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX}") @@ -1773,14 +1775,14 @@ if (PACKAGE) list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/plugins/gstreamer010/${CMAKE_CFG_INTDIR}") list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/plugins/quicktime/${CMAKE_CFG_INTDIR}") list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/plugins/webkit/${CMAKE_CFG_INTDIR}") - set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-darwin.tar.bz2") + set(VIEWER_SYMBOL_FILE "${SYMBOL_NAME}-darwin.tar.bz2") set(VIEWER_EXE_GLOBS "'${VIEWER_BRANDING_NAME}' SLPlugin") set(VIEWER_LIB_GLOB "*.dylib") set(VIEWER_DUMP_SYMS dump_syms) endif (DARWIN) if (LINUX) list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/packaged") - set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-linux.tar.bz2") + set(VIEWER_SYMBOL_FILE "${SYMBOL_NAME}-linux-${ARCH}.tar.bz2") set(VIEWER_EXE_GLOBS "${VIEWER_BRANDING_ID}-do-not-run-directly SLPlugin") set(VIEWER_LIB_GLOB "*${CMAKE_SHARED_MODULE_SUFFIX}*") set(VIEWER_COPY_MANIFEST copy_l_viewer_manifest) From 2d2513369a5ef77d509f54aee0d4c7488093f296 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 28 Dec 2014 00:20:30 -0600 Subject: [PATCH 44/55] OSX poking. --- indra/cmake/LLSharedLibs.cmake | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/indra/cmake/LLSharedLibs.cmake b/indra/cmake/LLSharedLibs.cmake index 8d31d9e77..982b2d3f7 100644 --- a/indra/cmake/LLSharedLibs.cmake +++ b/indra/cmake/LLSharedLibs.cmake @@ -1,17 +1,13 @@ # ll_deploy_sharedlibs_command # target_exe: the cmake target of the executable for which the shared libs will be deployed. macro(ll_deploy_sharedlibs_command target_exe) - SET(TARGET_LOCATION $) - get_filename_component(OUTPUT_PATH ${TARGET_LOCATION} PATH) + SET(OUTPUT_PATH $) if(DARWIN) SET_TEST_PATH(SEARCH_DIRS) get_target_property(IS_BUNDLE ${target_exe} MACOSX_BUNDLE) if(IS_BUNDLE) # If its a bundle the exe is not in the target location, this should find it. - get_filename_component(TARGET_FILE ${TARGET_LOCATION} NAME) - set(OUTPUT_PATH ${TARGET_LOCATION}.app/Contents/MacOS) - set(TARGET_LOCATION ${OUTPUT_PATH}/${TARGET_FILE}) set(OUTPUT_PATH ${OUTPUT_PATH}/../Resources) endif(IS_BUNDLE) elseif(WINDOWS) @@ -26,7 +22,7 @@ macro(ll_deploy_sharedlibs_command target_exe) TARGET ${target_exe} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS - "-DBIN_NAME=\"${TARGET_LOCATION}\"" + "-DBIN_NAME=\"$\"" "-DSEARCH_DIRS=\"${SEARCH_DIRS}\"" "-DDST_PATH=\"${OUTPUT_PATH}\"" "-P" From a891d2fdea75076b543d34291a94c80fbd09dde5 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Mon, 29 Dec 2014 18:59:15 -0600 Subject: [PATCH 45/55] FMOD(STUDIO|EX)_SDK_DIR wasn't being properly set. Change fallback on missing msvc*, as the old code made no sense (wildcarded paths don't throw when not found) --- indra/cmake/FMODEX.cmake | 24 +++++++----------------- indra/cmake/FMODSTUDIO.cmake | 24 +++++++----------------- indra/newview/viewer_manifest.py | 21 +++++++++++---------- 3 files changed, 25 insertions(+), 44 deletions(-) diff --git a/indra/cmake/FMODEX.cmake b/indra/cmake/FMODEX.cmake index b38f654ee..9f4cef95f 100644 --- a/indra/cmake/FMODEX.cmake +++ b/indra/cmake/FMODEX.cmake @@ -8,16 +8,12 @@ endif (FMODEX AND FMODSTUDIO) unset(FMOD_LIBRARY_RELEASE CACHE) unset(FMOD_LIBRARY_DEBUG CACHE) -unset(FMOD_LINK_LIBRARY_RELEASE CACHE) -unset(FMOD_LINK_LIBRARY_DEBUG CACHE) unset(FMOD_INCLUDE_DIR CACHE) -if (NOT FMODEX_SDK_DIR) - set(FMODEX_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.") - if(WINDOWS) - GET_FILENAME_COMPONENT(FMODEX_SDK_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Programmers\ API\ Windows] ABSOLUTE CACHE) - endif(WINDOWS) -endif (NOT FMODEX_SDK_DIR) +if (NOT FMODEX_SDK_DIR AND WINDOWS) + GET_FILENAME_COMPONENT(REG_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Programmers\ API\ Windows] ABSOLUTE) + set(FMODEX_SDK_DIR ${REG_DIR} CACHE PATH "Path to the FMOD Ex SDK." FORCE) +endif (NOT FMODEX_SDK_DIR AND WINDOWS) set(release_fmod_lib_paths ${LIBS_PREBUILT_DIR}/release/lib/ @@ -64,22 +60,16 @@ endif(WINDOWS) find_path(FMOD_INCLUDE_DIR fmod.hpp ${fmod_inc_paths}) if (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) - set(FMOD ON CACHE BOOL "Use closed source FMOD sound library.") + set(FMOD ON) if (NOT FMOD_LIBRARY_DEBUG) #Use release library in debug configuration if debug library is absent. set(FMOD_LIBRARY_DEBUG ${FMOD_LIBRARY_RELEASE}) endif (NOT FMOD_LIBRARY_DEBUG) else (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) - unset(FMOD_LIBRARY_RELEASE CACHE) - unset(FMOD_LIBRARY_DEBUG CACHE) - unset(FMOD_INCLUDE_DIR CACHE) - if (FMOD) - message(STATUS "No support for FMOD Ex audio (need to set FMODEX_SDK_DIR?)") - endif (FMOD) - set(FMOD OFF CACHE BOOL "Use closed source FMOD sound library.") + message(STATUS "No support for FMOD Ex audio (need to set FMODEX_SDK_DIR?)") set(FMOD OFF) set(FMODEX OFF) endif (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) if (FMOD) - message(STATUS "Building with FMOD audio support") + message(STATUS "Building with FMOD Ex audio support") endif (FMOD) diff --git a/indra/cmake/FMODSTUDIO.cmake b/indra/cmake/FMODSTUDIO.cmake index 2b0daabfe..688d28e97 100644 --- a/indra/cmake/FMODSTUDIO.cmake +++ b/indra/cmake/FMODSTUDIO.cmake @@ -8,16 +8,12 @@ endif (FMODEX AND FMODSTUDIO) unset(FMOD_LIBRARY_RELEASE CACHE) unset(FMOD_LIBRARY_DEBUG CACHE) -unset(FMOD_LINK_LIBRARY_RELEASE CACHE) -unset(FMOD_LINK_LIBRARY_DEBUG CACHE) unset(FMOD_INCLUDE_DIR CACHE) -if (NOT FMODSTUDIO_SDK_DIR) - set(FMODSTUDIO_SDK_DIR CACHE PATH "Path to the FMOD Studio SDK.") - if(WINDOWS) - GET_FILENAME_COMPONENT(FMODSTUDIO_SDK_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Studio\ API\ Windows] ABSOLUTE CACHE) - endif(WINDOWS) -endif (NOT FMODSTUDIO_SDK_DIR) +if (NOT FMODSTUDIO_SDK_DIR AND WINDOWS) + GET_FILENAME_COMPONENT(REG_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Studio\ API\ Windows] ABSOLUTE) + set(FMODSTUDIO_SDK_DIR ${REG_DIR} CACHE PATH "Path to the FMOD Studio SDK." FORCE) +endif (NOT FMODSTUDIO_SDK_DIR AND WINDOWS) set(release_fmod_lib_paths ${LIBS_PREBUILT_DIR}/release/lib/ @@ -67,22 +63,16 @@ endif(WINDOWS) find_path(FMOD_INCLUDE_DIR fmod.hpp ${fmod_inc_paths}) if (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) - set(FMOD ON CACHE BOOL "Use closed source FMOD sound library.") + set(FMOD ON) if (NOT FMOD_LIBRARY_DEBUG) #Use release library in debug configuration if debug library is absent. set(FMOD_LIBRARY_DEBUG ${FMOD_LIBRARY_RELEASE}) endif (NOT FMOD_LIBRARY_DEBUG) else (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) - unset(FMOD_LIBRARY_RELEASE CACHE) - unset(FMOD_LIBRARY_DEBUG CACHE) - unset(FMOD_INCLUDE_DIR CACHE) - if (FMOD) - message(STATUS "No support for FMOD Studio audio (need to set FMODSTUDIO_SDK_DIR?)") - endif (FMOD) - set(FMOD OFF CACHE BOOL "Use closed source FMOD sound library.") + message(STATUS "No support for FMOD Studio audio (need to set FMODSTUDIO_SDK_DIR?)") set(FMOD OFF) set(FMODSTUDIO OFF) endif (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) if (FMOD) - message(STATUS "Building with FMOD audio support") + message(STATUS "Building with FMOD Studio audio support") endif (FMOD) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index e33bbf6fa..3bdcc22d3 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -264,16 +264,17 @@ class WindowsManifest(ViewerManifest): self.add_extra_libraries() - try: - self.path("msvc*.dll") - except: - try: - if self.prefix(src="msvcrt", dst=""): - self.path("*.dll") - self.path("*.manifest") - self.end_prefix() - except: - pass + if(self.prefix(src="..", dst="")): + found_files = self.path("msvc*.dll") + self.end_prefix() + if(not found_files): + try: + if self.prefix(src="msvcrt", dst=""): + self.path("*.dll") + self.path("*.manifest") + self.end_prefix() + except: + pass # Vivox runtimes self.path("SLVoice.exe") From 7943adedaaf452b0b89ab60704b096608c61f946 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Mon, 5 Jan 2015 14:25:55 -0600 Subject: [PATCH 46/55] Also need this. Last build only succeeded due to lingering obsolete entries in the cmake var cache. --- indra/llaudio/CMakeLists.txt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt index 943bd9f03..25970a99a 100644 --- a/indra/llaudio/CMakeLists.txt +++ b/indra/llaudio/CMakeLists.txt @@ -17,13 +17,9 @@ include(LLMath) include(LLMessage) include(LLVFS) -if (FMODSTUDIO) - include_directories(${FMODSTUDIO_INCLUDE_DIR}) -endif(FMODSTUDIO) - -if (FMODEX) - include_directories(${FMODEX_INCLUDE_DIR}) -endif(FMODEX) +if (FMOD) + include_directories(${FMOD_INCLUDE_DIR}) +endif(FMOD) include_directories( ${LLAUDIO_INCLUDE_DIRS} From 1e0395c26dae55270eb903293878e5227ec9c833 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Mon, 5 Jan 2015 17:02:00 -0600 Subject: [PATCH 47/55] Back to boost mutexes --- indra/llcommon/llthread.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 9ad63bbdd..ec05fe51e 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -27,7 +27,7 @@ #ifndef LL_LLTHREAD_H #define LL_LLTHREAD_H -#define USE_BOOST_MUTEX 0 +#define USE_BOOST_MUTEX 1 #define IS_LLCOMMON_INLINE (!LL_COMMON_LINK_SHARED || defined(llcommon_EXPORTS)) From 1a9cd725b2d1683dd9ba8c8adb0fdd78c5d3757a Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 6 Jan 2015 03:56:57 -0600 Subject: [PATCH 48/55] boost::mutex seems slow with vs2010, so fall back to apr_mutex for it. --- indra/llcommon/llthread.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index ec05fe51e..62e36c2e4 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -27,7 +27,9 @@ #ifndef LL_LLTHREAD_H #define LL_LLTHREAD_H +#if !defined(_MSC_VER) || _MSC_VER >= 1700 #define USE_BOOST_MUTEX 1 +#endif #define IS_LLCOMMON_INLINE (!LL_COMMON_LINK_SHARED || defined(llcommon_EXPORTS)) From b1954e411b8a60d2ef6c39ea1dc7adebf0b114c6 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 8 Jan 2015 00:46:36 -0600 Subject: [PATCH 49/55] Nice typos. --- indra/cmake/FMODEX.cmake | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/indra/cmake/FMODEX.cmake b/indra/cmake/FMODEX.cmake index 9f4cef95f..a7d790581 100644 --- a/indra/cmake/FMODEX.cmake +++ b/indra/cmake/FMODEX.cmake @@ -22,11 +22,9 @@ set(debug_fmod_lib_paths ${LIBS_PREBUILT_DIR}/debug/lib ${LIBS_PREBUILT_LEGACY_DIR}/debug/lib) set(fmod_inc_paths - ${LIBS_PREBUILT_DIR}/include/fmodsex + ${LIBS_PREBUILT_DIR}/include/fmodex ${LIBS_PREBUILT_LEGACY_DIR}/include/fmodex) - - if (FMODEX_SDK_DIR) set(release_fmod_lib_paths ${release_fmod_lib_paths} "${FMODEX_SDK_DIR}/api" "${FMODEX_SDK_DIR}/api/lib") set(debug_fmod_lib_paths ${debug_fmod_lib_paths} "${FMODEX_SDK_DIR}/api" "${FMODEX_SDK_DIR}/api/lib") @@ -54,8 +52,8 @@ if(WINDOWS) find_library(FMOD_LINK_LIBRARY_DEBUG fmodLex64_vc PATHS ${debug_fmod_lib_paths}) endif (WORD_SIZE EQUAL 32) else(WINDOWS) - set(FMODSTUDIO_LINK_LIBRARY_RELEASE ${FMODSTUDIO_LIBRARY_RELEASE}) - set(FMODSTUDIO_LINK_LIBRARY_DEBUG ${FMODSTUDIO_LIBRARY_DEBUG}) + set(FMOD_LINK_LIBRARY_RELEASE ${FMODSTUDIO_LIBRARY_RELEASE}) + set(FMOD_LINK_LIBRARY_DEBUG ${FMODSTUDIO_LIBRARY_DEBUG}) endif(WINDOWS) find_path(FMOD_INCLUDE_DIR fmod.hpp ${fmod_inc_paths}) From 2678d0f99e108bb1a1562e1877dbe1a4a3307b53 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 8 Jan 2015 00:49:42 -0600 Subject: [PATCH 50/55] Trying fmodstudio as a prebuilt. (pushed to test on linux buildserver) --- indra/cmake/FMODSTUDIO.cmake | 121 +++++++++++++++++++++---------- indra/newview/viewer_manifest.py | 32 ++++---- install.xml | 49 ++++++++++++- 3 files changed, 150 insertions(+), 52 deletions(-) diff --git a/indra/cmake/FMODSTUDIO.cmake b/indra/cmake/FMODSTUDIO.cmake index 688d28e97..b2460cc79 100644 --- a/indra/cmake/FMODSTUDIO.cmake +++ b/indra/cmake/FMODSTUDIO.cmake @@ -10,24 +10,17 @@ unset(FMOD_LIBRARY_RELEASE CACHE) unset(FMOD_LIBRARY_DEBUG CACHE) unset(FMOD_INCLUDE_DIR CACHE) -if (NOT FMODSTUDIO_SDK_DIR AND WINDOWS) - GET_FILENAME_COMPONENT(REG_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Studio\ API\ Windows] ABSOLUTE) - set(FMODSTUDIO_SDK_DIR ${REG_DIR} CACHE PATH "Path to the FMOD Studio SDK." FORCE) -endif (NOT FMODSTUDIO_SDK_DIR AND WINDOWS) +if(STANDALONE) + if (NOT FMODSTUDIO_SDK_DIR AND WINDOWS) + GET_FILENAME_COMPONENT(REG_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Studio\ API\ Windows] ABSOLUTE) + set(FMODSTUDIO_SDK_DIR ${REG_DIR} CACHE PATH "Path to the FMOD Studio SDK." FORCE) + endif (NOT FMODSTUDIO_SDK_DIR AND WINDOWS) + if(NOT FMODSTUDIO_SDK_DIR) + message(FATAL_ERROR "FMODSTUDIO_SDK_DIR not set!") + endif(NOT FMODSTUDIO_SDK_DIR) +endif(STANDALONE) -set(release_fmod_lib_paths - ${LIBS_PREBUILT_DIR}/release/lib/ - ${LIBS_PREBUILT_LEGACY_DIR}/release/lib) -set(debug_fmod_lib_paths - ${LIBS_PREBUILT_DIR}/debug/lib - ${LIBS_PREBUILT_LEGACY_DIR}/debug/lib) -set(fmod_inc_paths - ${LIBS_PREBUILT_DIR}/include/fmodstudio - ${LIBS_PREBUILT_LEGACY_DIR}/include/fmodstudio) - - - -if (FMODSTUDIO_SDK_DIR) +if(FMODSTUDIO_SDK_DIR) if(LINUX AND WORD_SIZE EQUAL 32) set(release_lib_paths ${release_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/x86/lib" ) set(debug__lib_paths ${debug_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/x86/lib") @@ -39,30 +32,82 @@ if (FMODSTUDIO_SDK_DIR) set(debug_fmod_lib_paths ${debug_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib") endif(LINUX AND WORD_SIZE EQUAL 32) set(fmod_inc_paths ${fmod_inc_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/inc") + + if(LINUX AND WORD_SIZE EQUAL 32) + set(release_lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib/x86" ) + set(debug__lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lig/x86") + elseif(LINUX) + set(release__lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib/x86_64") + set(debug_fmod_lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib/x86_64") + else(LINUX AND WORD_SIZE EQUAL 32) + set(release_fmod_lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib") + set(debug_fmod_lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib") + endif(LINUX AND WORD_SIZE EQUAL 32) + set(fmod_inc_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/inc") + + if(WINDOWS) + set(CMAKE_FIND_LIBRARY_SUFFIXES_OLD ${CMAKE_FIND_LIBRARY_SUFFIXES}) + set(CMAKE_FIND_LIBRARY_SUFFIXES .dll) + endif(WINDOWS) + if(WORD_SIZE EQUAL 64 AND WINDOWS) + find_library(FMOD_LIBRARY_RELEASE fmod64 PATHS ${release_fmod_lib_paths}) + find_library(FMOD_LIBRARY_DEBUG fmodL64 PATHS ${debug_fmod_lib_paths}) + else(WORD_SIZE EQUAL 64 AND WINDOWS)#Check if CMAKE_FIND_LIBRARY_PREFIXES is set to 'lib' for darwin. + find_library(FMOD_LIBRARY_RELEASE fmod PATHS ${release_fmod_lib_paths}) + find_library(FMOD_LIBRARY_DEBUG fmodL PATHS ${debug_fmod_lib_paths}) + endif(WORD_SIZE EQUAL 64 AND WINDOWS) + if(WINDOWS) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_OLD}) + string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) + string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) + else(WINDOWS) + set(FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) + set(FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) + endif(WINDOWS) + find_path(FMOD_INCLUDE_DIR fmod.hpp ${fmod_inc_paths}) + if(NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) + if(STANDALONE) + message(FATAL_ERROR "Provided FMODSTUDIO_SDK_DIR path not found '{$FMODSTUDIO_SDK_DIR}'") + else(STANDALONE) + message(STATUS "Provided FMODSTUDIO_SDK_DIR path not found '${FMODSTUDIO_SDK_DIR}'. Falling back to prebuilts") + endif(STANDALONE) + else(NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) + message(STATUS "Using system-provided FMOD Studio Libraries") + endif(NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) endif (FMODSTUDIO_SDK_DIR) -if(WINDOWS) - set(CMAKE_FIND_LIBRARY_SUFFIXES_OLD ${CMAKE_FIND_LIBRARY_SUFFIXES}) - set(CMAKE_FIND_LIBRARY_SUFFIXES .dll) -endif(WINDOWS) -if(WORD_SIZE EQUAL 32) #Check if CMAKE_FIND_LIBRARY_PREFIXES is set to 'lib' for darwin. - find_library(FMOD_LIBRARY_RELEASE fmod PATHS ${release_fmod_lib_paths}) - find_library(FMOD_LIBRARY_DEBUG fmodL PATHS ${debug_fmod_lib_paths}) -elseif(WORD_SIZE EQUAL 64) - find_library(FMOD_LIBRARY_RELEASE fmod64 PATHS ${release_fmod_lib_paths}) - find_library(FMOD_LIBRARY_DEBUG fmodL64 PATHS ${debug_fmod_lib_paths}) -endif (WORD_SIZE EQUAL 32) -if(WINDOWS) - set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_OLD}) - string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) - string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) -else(WINDOWS) - set(FMODSTUDIO_LINK_LIBRARY_RELEASE ${FMODSTUDIO_LIBRARY_RELEASE}) - set(FMODSTUDIO_LINK_LIBRARY_DEBUG ${FMODSTUDIO_LIBRARY_DEBUG}) -endif(WINDOWS) -find_path(FMOD_INCLUDE_DIR fmod.hpp ${fmod_inc_paths}) +if (NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) + if(WINDOWS) + set(lib_suffix .dll) + elseif(DARWIN) + set(lib_suffix .dynlib) + else(WINDOWS) + set(lib_suffix .so) + endif(WINDOWS) + if(WINDOWS) + if(WORD_SIZE EQUAL 64) + set(FMOD_LIBRARY_RELEASE ${LIBS_PREBUILT_DIR}/lib/release/fmod64${lib_suffix}) + set(FMOD_LIBRARY_DEBUG ${LIBS_PREBUILT_DIR}/lib/debug/fmodL64${lib_suffix}) + else(WORD_SIZE EQUAL 64) + set(FMOD_LIBRARY_RELEASE ${LIBS_PREBUILT_DIR}/lib/release/fmod${lib_suffix}) + set(FMOD_LIBRARY_DEBUG ${LIBS_PREBUILT_DIR}/lib/debug/fmodL${lib_suffix}) + endif(WORD_SIZE EQUAL 64) + else(WINDOWS) + set(FMOD_LIBRARY_RELEASE ${LIBS_PREBUILT_DIR}/lib/release/libfmod${lib_suffix}) + set(FMOD_LIBRARY_DEBUG ${LIBS_PREBUILT_DIR}/lib/debug/libfmodL${lib_suffix}) + endif(WINDOWS) + set(FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) + set(FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) + if(WINDOWS) + string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) + string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) + endif(WINDOWS) + use_prebuilt_binary(fmodstudio) + set(FMOD_INCLUDE_DIR + ${LIBS_PREBUILT_DIR}/include/fmodstudio) +endif(NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) -if (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) +if(FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) set(FMOD ON) if (NOT FMOD_LIBRARY_DEBUG) #Use release library in debug configuration if debug library is absent. set(FMOD_LIBRARY_DEBUG ${FMOD_LIBRARY_RELEASE}) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 3bdcc22d3..5d95a4d5e 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -576,8 +576,10 @@ class DarwinManifest(ViewerManifest): ): self.path(libfile) - dylibs += self.add_extra_libraries() + self.end_prefix() + if self.prefix(src=self.args['configuration'], alt_build=libdir): + dylibs += self.add_extra_libraries() self.end_prefix() # our apps @@ -876,18 +878,20 @@ class Linux_i686Manifest(LinuxManifest): self.path("libboost_system-mt.so.*") self.path("libboost_thread-mt.so.*") - self.add_extra_libraries() - self.end_prefix("lib") + + if (not self.standalone()) and self.prefix(src=self.args['configuration'], alt_build="../packages/lib/release", dst="lib"): + dylibs += self.add_extra_libraries() + self.end_prefix() - # Vivox runtimes - if self.prefix(src="../packages/lib/release", dst="bin"): - self.path("SLVoice") - self.end_prefix("bin") - if self.prefix(src="../packages/lib/release", dst="lib"): - self.path("libortp.so") - self.path("libvivoxsdk.so") - self.end_prefix("lib") + # Vivox runtimes + if self.prefix(src="../packages/lib/release", dst="bin"): + self.path("SLVoice") + self.end_prefix("bin") + if self.prefix(src="../packages/lib/release", dst="lib"): + self.path("libortp.so") + self.path("libvivoxsdk.so") + self.end_prefix("lib") class Linux_x86_64Manifest(LinuxManifest): def construct(self): @@ -926,10 +930,12 @@ class Linux_x86_64Manifest(LinuxManifest): self.path("libboost_system-mt.so.*") self.path("libboost_thread-mt.so.*") - self.add_extra_libraries() - self.end_prefix("lib") + if (not self.standalone()) and self.prefix(src=self.args['configuration'], alt_build="../packages/lib/release", dst="lib"): + dylibs += self.add_extra_libraries() + self.end_prefix() + # Vivox runtimes if self.prefix(src="../packages/lib/release", dst="bin"): self.path("SLVoice") diff --git a/install.xml b/install.xml index 963a1b361..055d6ca05 100644 --- a/install.xml +++ b/install.xml @@ -450,6 +450,53 @@ https://bitbucket.org/SingularityViewer/libraries/downloads/expat-2.1.0-windows64-vs12-20140709.tar.bz2 + + fmodstudio + + copyright + Copyright © 1994-2014 Firelight Technologies Pty, Ltd. + description + FMOD Studio programmer’s API's low level component. For simple sounds/channels/dsp/geometry. + license + fmod + packages + + darwin + + md5sum + 93ebcfcaf0572427593cdb38d7c7435b + url + https://bitbucket.org/SingularityViewer/libraries/downloads/fmodstudio-1.05.09-darwin-20150106.tar.bz2 + + linux + + md5sum + b75bcb905747b6aa9c288924379741d4 + url + https://bitbucket.org/SingularityViewer/libraries/downloads/fmodstudio-1.05.09-linux-20150106.tar.bz2 + + linux64 + + md5sum + 0d50965abbd4ed317f221761abc25b55 + url + https://bitbucket.org/SingularityViewer/libraries/downloads/fmodstudio-1.05.09-linux64-20150106.tar.bz2 + + windows + + md5sum + c24c02a6130cd9032077c08905e89e88 + url + https://bitbucket.org/SingularityViewer/libraries/downloads/fmodstudio-1.05.09-windows-20150106.tar.bz2 + + windows64 + + md5sum + 72065eb2a8ebb7d4ef12c320f1f85780 + url + https://bitbucket.org/SingularityViewer/libraries/downloads/fmodstudio-1.05.09-windows64-20150106.tar.bz2 + + fontconfig @@ -1721,7 +1768,7 @@ your work. fmod url - http://www.fmod.org/ifmodlicense.html + http://www.fmod.com/files/public/LICENSE.TXT freetype From b808caaa0ef4e26467caa8eccf2b98ede1567054 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Fri, 9 Jan 2015 21:38:27 -0600 Subject: [PATCH 51/55] Appease cmake 3.1.0 --- indra/cmake/DirectX.cmake | 47 ++++++++++++++++++------------- indra/cmake/QuickTimePlugin.cmake | 9 ++++-- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/indra/cmake/DirectX.cmake b/indra/cmake/DirectX.cmake index 97a0bf910..56547bb7d 100644 --- a/indra/cmake/DirectX.cmake +++ b/indra/cmake/DirectX.cmake @@ -11,25 +11,32 @@ if (WINDOWS) set (DIRECTX_ARCHITECTURE x86) endif (WORD_SIZE EQUAL 32) + SET(program_files $ENV{ProgramW6432}) + if(NOT program_files) + SET(program_files $ENV{ProgramFiles}) + endif(NOT program_files) + SET(program_files_x86 "ProgramFiles(x86)") + SET(program_files_x86 $ENV{${program_files_x86}}) + find_path(DIRECTX_ROOT_DIR Include/dxdiag.h PATHS "$ENV{DXSDK_DIR}" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (June 2010)" - "$ENV{ProgramFiles(x86)}/Microsoft DirectX SDK (June 2010)" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (February 2010)" - "$ENV{ProgramFiles(x86)}/Microsoft DirectX SDK (February 2010)" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (March 2009)" - "$ENV{ProgramFiles(x86)}/Microsoft DirectX SDK (March 2009)" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (August 2008)" - "$ENV{ProgramFiles(x86)}/Microsoft DirectX SDK (August 2008)" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (June 2008)" - "$ENV{ProgramFiles(x86)}/Microsoft DirectX SDK (June 2008)" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (March 2008)" - "$ENV{ProgramFiles(x86)}/Microsoft DirectX SDK (March 2008)" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (November 2007)" - "$ENV{ProgramFiles(x86)}/Microsoft DirectX SDK (November 2007)" - "$ENV{ProgramFiles}/Microsoft DirectX SDK (August 2007)" - "$ENV{ProgramFiles(x86)}/Microsoft DirectX SDK (August 2007)" + "${program_files}/Microsoft DirectX SDK (June 2010)" + "${program_files_x86}/Microsoft DirectX SDK (June 2010)" + "${program_files}/Microsoft DirectX SDK (February 2010)" + "${program_files_x86}/Microsoft DirectX SDK (February 2010)" + "${program_files}/Microsoft DirectX SDK (March 2009)" + "${program_files_x86}/Microsoft DirectX SDK (March 2009)" + "${program_files}/Microsoft DirectX SDK (August 2008)" + "${program_files_x86}/Microsoft DirectX SDK (August 2008)" + "${program_files}/Microsoft DirectX SDK (June 2008)" + "${program_files_x86}/Microsoft DirectX SDK (June 2008)" + "${program_files}/Microsoft DirectX SDK (March 2008)" + "${program_files_x86}/Microsoft DirectX SDK (March 2008)" + "${program_files}/Microsoft DirectX SDK (November 2007)" + "${program_files_x86}/Microsoft DirectX SDK (November 2007)" + "${program_files}/Microsoft DirectX SDK (August 2007)" + "${program_files_x86}/Microsoft DirectX SDK (August 2007)" ) if (DIRECTX_ROOT_DIR) @@ -38,10 +45,10 @@ if (WINDOWS) else (DIRECTX_ROOT_DIR) find_path (WIN_KIT_ROOT_DIR Include/um/windows.h PATHS - "$ENV{ProgramFiles}/Windows Kits/8.1" - "$ENV{ProgramFiles(x86)}/Windows Kits/8.1" - "$ENV{ProgramFiles}/Windows Kits/8.0" - "$ENV{ProgramFiles(x86)}/Windows Kits/8.0" + "${program_files}/Windows Kits/8.1" + "${program_files_x86}/Windows Kits/8.1" + "${program_files}/Windows Kits/8.0" + "${program_files_x86}/Windows Kits/8.0" ) find_path (WIN_KIT_LIB_DIR dxguid.lib diff --git a/indra/cmake/QuickTimePlugin.cmake b/indra/cmake/QuickTimePlugin.cmake index d9317a25b..b769b29ba 100644 --- a/indra/cmake/QuickTimePlugin.cmake +++ b/indra/cmake/QuickTimePlugin.cmake @@ -8,8 +8,13 @@ endif(INSTALL_PROPRIETARY) if (DARWIN) include(CMakeFindFrameworks) find_library(QUICKTIME_LIBRARY QuickTime) -elseif (WINDOWS AND WORD_SIZE EQUAL 32) - set(QUICKTIME_SDK_DIR "$ENV{PROGRAMFILES}/QuickTime SDK" + + SET(program_files $ENV{ProgramW6432}) + if(NOT program_files) + SET(program_files $ENV{ProgramFiles}) + endif(NOT program_files) + + set(QUICKTIME_SDK_DIR "${program_files}/QuickTime SDK" CACHE PATH "Location of the QuickTime SDK.") find_library(DEBUG_QUICKTIME_LIBRARY qtmlclient From c708408f64b53041e4ac284a5bbf40c829439462 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sat, 10 Jan 2015 16:11:52 -0600 Subject: [PATCH 52/55] Linux OSS was removed from fmodstudio. Also fix issue in linux manifest. --- indra/llaudio/llaudioengine_fmodstudio.cpp | 23 ---------------------- indra/newview/viewer_manifest.py | 4 ++-- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index 47d54fd3b..93541d3de 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -368,27 +368,6 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) } } if (!audio_ok) - { - if (NULL == getenv("LL_BAD_FMOD_OSS")) /*Flawfinder: ignore*/ - { - LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL; - if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_OSS)) == FMOD_OK && - (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) - { - LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL; - audio_ok = true; - } - else - { - Check_FMOD_Error(result, "OSS audio output FAILED to initialize"); - } - } - else - { - LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL; - } - } - if (!audio_ok) { LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL; return false; @@ -407,8 +386,6 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) LL_INFOS("AppInit") << "Audio output: PulseAudio" << LL_ENDL; break; case FMOD_OUTPUTTYPE_ALSA: LL_INFOS("AppInit") << "Audio output: ALSA" << LL_ENDL; break; - case FMOD_OUTPUTTYPE_OSS: - LL_INFOS("AppInit") << "Audio output: OSS" << LL_ENDL; break; default: LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break; }; diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 5d95a4d5e..e7b3268fa 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -881,7 +881,7 @@ class Linux_i686Manifest(LinuxManifest): self.end_prefix("lib") if (not self.standalone()) and self.prefix(src=self.args['configuration'], alt_build="../packages/lib/release", dst="lib"): - dylibs += self.add_extra_libraries() + self.add_extra_libraries() self.end_prefix() # Vivox runtimes @@ -933,7 +933,7 @@ class Linux_x86_64Manifest(LinuxManifest): self.end_prefix("lib") if (not self.standalone()) and self.prefix(src=self.args['configuration'], alt_build="../packages/lib/release", dst="lib"): - dylibs += self.add_extra_libraries() + self.add_extra_libraries() self.end_prefix() # Vivox runtimes From ca98523c4189c1f11de0a5419b4051b43cba4c43 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 22 Jan 2015 02:39:45 -0600 Subject: [PATCH 53/55] Cmake version bump, cleanup, typo fixes, rewrite of fmodex.cmake. --- indra/CMakeLists.txt | 2 +- indra/cmake/FMODEX.cmake | 133 ++++++++++++++++++++----------- indra/cmake/FMODSTUDIO.cmake | 75 ++++++++--------- indra/cmake/OPENAL.cmake | 3 + indra/develop.py | 4 +- indra/newview/CMakeLists.txt | 14 +--- indra/newview/viewer_manifest.py | 13 +-- 7 files changed, 137 insertions(+), 107 deletions(-) diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index d213b0efd..8694c0dcc 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -4,7 +4,7 @@ # other commands to guarantee full compatibility # with the version specified -cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR) +cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR) # Eventually the third-party support modules (cmake/*.cmake) should # know the full path to all libraries. Until that happens we need diff --git a/indra/cmake/FMODEX.cmake b/indra/cmake/FMODEX.cmake index a7d790581..eb8712207 100644 --- a/indra/cmake/FMODEX.cmake +++ b/indra/cmake/FMODEX.cmake @@ -3,71 +3,114 @@ include(Linking) if (FMODEX AND FMODSTUDIO) - message( FATAL_ERROR "You can not enable two FMOD variants at the same time." ) + message( FATAL_ERROR "You can not enable two FMOD variants at the same time." ) endif (FMODEX AND FMODSTUDIO) unset(FMOD_LIBRARY_RELEASE CACHE) unset(FMOD_LIBRARY_DEBUG CACHE) unset(FMOD_INCLUDE_DIR CACHE) -if (NOT FMODEX_SDK_DIR AND WINDOWS) - GET_FILENAME_COMPONENT(REG_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Programmers\ API\ Windows] ABSOLUTE) - set(FMODEX_SDK_DIR ${REG_DIR} CACHE PATH "Path to the FMOD Ex SDK." FORCE) -endif (NOT FMODEX_SDK_DIR AND WINDOWS) +set(FMOD_EXTERNAL_LIB OFF) -set(release_fmod_lib_paths - ${LIBS_PREBUILT_DIR}/release/lib/ - ${LIBS_PREBUILT_LEGACY_DIR}/release/lib) -set(debug_fmod_lib_paths - ${LIBS_PREBUILT_DIR}/debug/lib - ${LIBS_PREBUILT_LEGACY_DIR}/debug/lib) -set(fmod_inc_paths - ${LIBS_PREBUILT_DIR}/include/fmodex - ${LIBS_PREBUILT_LEGACY_DIR}/include/fmodex) +if(STANDALONE OR WINDOWS) + if (NOT FMODEX_SDK_DIR AND WINDOWS) + GET_FILENAME_COMPONENT(REG_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Programmers\ API\ Windows] ABSOLUTE) + set(FMODEX_SDK_DIR ${REG_DIR} CACHE PATH "Path to the FMOD Ex SDK." FORCE) + endif (NOT FMODEX_SDK_DIR AND WINDOWS) + if(NOT FMODEX_SDK_DIR AND STANDALONE) + message(FATAL_ERROR "FMODEX_SDK_DIR not set!") + endif(NOT FMODEX_SDK_DIR AND STANDALONE) +endif(STANDALONE OR WINDOWS) -if (FMODEX_SDK_DIR) - set(release_fmod_lib_paths ${release_fmod_lib_paths} "${FMODEX_SDK_DIR}/api" "${FMODEX_SDK_DIR}/api/lib") - set(debug_fmod_lib_paths ${debug_fmod_lib_paths} "${FMODEX_SDK_DIR}/api" "${FMODEX_SDK_DIR}/api/lib") - set(fmod_inc_paths ${fmod_inc_paths} "${FMODEX_SDK_DIR}/api/inc") +if(FMODEX_SDK_DIR) + set(fmod_lib_paths "${FMODEX_SDK_DIR}/api" "${FMODEX_SDK_DIR}/api/lib" ) + set(fmod_inc_paths "${FMODEX_SDK_DIR}/api/inc") + + if(WINDOWS) + set(CMAKE_FIND_LIBRARY_SUFFIXES_OLD ${CMAKE_FIND_LIBRARY_SUFFIXES}) + set(CMAKE_FIND_LIBRARY_SUFFIXES .dll) + endif(WINDOWS) + if(WORD_SIZE EQUAL 64) + find_library(FMOD_LIBRARY_RELEASE fmodex64 PATHS ${fmod_lib_paths} NO_DEFAULT_PATH) + find_library(FMOD_LIBRARY_DEBUG fmodexL64 PATHS ${fmod_lib_paths} NO_DEFAULT_PATH) + else(WORD_SIZE EQUAL 64)#Check if CMAKE_FIND_LIBRARY_PREFIXES is set to 'lib' for darwin. + find_library(FMOD_LIBRARY_RELEASE fmodex PATHS ${fmod_lib_paths} NO_DEFAULT_PATH) + find_library(FMOD_LIBRARY_DEBUG fmodexL PATHS ${fmod_lib_paths} NO_DEFAULT_PATH) + endif(WORD_SIZE EQUAL 64) + if(WINDOWS) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_OLD}) + if(WORD_SIZE EQUAL 64) + find_library(FMOD_LINK_LIBRARY_RELEASE fmodex64_vc PATHS ${fmod_lib_paths} NO_DEFAULT_PATH) + find_library(FMOD_LINK_LIBRARY_DEBUG fmodexL64_vc PATHS ${fmod_lib_paths} NO_DEFAULT_PATH) + else(WORD_SIZE EQUAL 64)#Check if CMAKE_FIND_LIBRARY_PREFIXES is set to 'lib' for darwin. + find_library(FMOD_LINK_LIBRARY_RELEASE fmodex_vc PATHS ${fmod_lib_paths} NO_DEFAULT_PATH) + find_library(FMOD_LINK_LIBRARY_DEBUG fmodexL_vc PATHS ${fmod_lib_paths} NO_DEFAULT_PATH) + endif(WORD_SIZE EQUAL 64) + else(WINDOWS) + set(FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) + set(FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) + endif(WINDOWS) + find_path(FMOD_INCLUDE_DIR fmod.hpp ${fmod_inc_paths}) + if(NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) + if(STANDALONE) + message(FATAL_ERROR "Provided FMODEX_SDK_DIR path not found '{$FMODEX_SDK_DIR}'") + else(STANDALONE) + message(STATUS "Provided FMODEX_SDK_DIR path not found '${FMODEX_SDK_DIR}'. Falling back to prebuilts") + endif(STANDALONE) + else(NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) + message(STATUS "Using system-provided FMOD Ex Libraries") + set(FMOD_EXTERNAL_LIB ON) + endif(NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) endif (FMODEX_SDK_DIR) -if(WINDOWS) - set(CMAKE_FIND_LIBRARY_SUFFIXES_OLD ${CMAKE_FIND_LIBRARY_SUFFIXES}) - set(CMAKE_FIND_LIBRARY_SUFFIXES .dll) -endif(WINDOWS) -if(WORD_SIZE EQUAL 32) #Check if CMAKE_FIND_LIBRARY_PREFIXES is set to 'lib' for darwin. - find_library(FMOD_LIBRARY_RELEASE fmodex PATHS ${release_fmod_lib_paths}) - find_library(FMOD_LIBRARY_DEBUG fmodexL PATHS ${debug_fmod_lib_paths}) -elseif(WORD_SIZE EQUAL 64) - find_library(FMOD_LIBRARY_RELEASE fmodex64 PATHS ${release_fmod_lib_paths}) - find_library(FMOD_LIBRARY_DEBUG fmodLex64 PATHS ${debug_fmod_lib_paths}) -endif (WORD_SIZE EQUAL 32) -if(WINDOWS) - set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_OLD}) - if(WORD_SIZE EQUAL 32) - find_library(FMOD_LINK_LIBRARY_RELEASE fmodex_vc PATHS ${release_fmod_lib_paths}) - find_library(FMOD_LINK_LIBRARY_DEBUG fmodexL_vc PATHS ${debug_fmod_lib_paths}) - elseif(WORD_SIZE EQUAL 64) - find_library(FMOD_LINK_LIBRARY_RELEASE fmodex64_vc PATHS ${release_fmod_lib_paths}) - find_library(FMOD_LINK_LIBRARY_DEBUG fmodLex64_vc PATHS ${debug_fmod_lib_paths}) - endif (WORD_SIZE EQUAL 32) -else(WINDOWS) - set(FMOD_LINK_LIBRARY_RELEASE ${FMODSTUDIO_LIBRARY_RELEASE}) - set(FMOD_LINK_LIBRARY_DEBUG ${FMODSTUDIO_LIBRARY_DEBUG}) -endif(WINDOWS) -find_path(FMOD_INCLUDE_DIR fmod.hpp ${fmod_inc_paths}) +if (NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) + if(WINDOWS) + set(lib_suffix .dll) + elseif(DARWIN) + set(lib_suffix .dynlib) + else(WINDOWS) + set(lib_suffix .so) + endif(WINDOWS) + if(WINDOWS) + if(WORD_SIZE EQUAL 64) + set(FMOD_LIBRARY_RELEASE ${LIBS_PREBUILT_DIR}/lib/release/fmodex64${lib_suffix}) + set(FMOD_LIBRARY_DEBUG ${LIBS_PREBUILT_DIR}/lib/debug/fmodexL64${lib_suffix}) + else(WORD_SIZE EQUAL 64) + set(FMOD_LIBRARY_RELEASE ${LIBS_PREBUILT_DIR}/lib/release/fmodex${lib_suffix}) + set(FMOD_LIBRARY_DEBUG ${LIBS_PREBUILT_DIR}/lib/debug/fmodexL${lib_suffix}) + endif(WORD_SIZE EQUAL 64) + else(WINDOWS) + if(WORD_SIZE EQUAL 64) + set(FMOD_LIBRARY_RELEASE ${LIBS_PREBUILT_DIR}/lib/release/libfmodex64${lib_suffix}) + set(FMOD_LIBRARY_DEBUG ${LIBS_PREBUILT_DIR}/lib/debug/libfmodex64L${lib_suffix}) + else(WORD_SIZE EQUAL 64) + set(FMOD_LIBRARY_RELEASE ${LIBS_PREBUILT_DIR}/lib/release/libfmodex${lib_suffix}) + set(FMOD_LIBRARY_DEBUG ${LIBS_PREBUILT_DIR}/lib/debug/libfmodexL${lib_suffix}) + endif(WORD_SIZE EQUAL 64) + endif(WINDOWS) + set(FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) + set(FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) + if(WINDOWS) + string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) + string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) + endif(WINDOWS) + use_prebuilt_binary(fmodex) + set(FMOD_INCLUDE_DIR + ${LIBS_PREBUILT_DIR}/include/fmodex) +endif(NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) -if (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) +if(FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) set(FMOD ON) if (NOT FMOD_LIBRARY_DEBUG) #Use release library in debug configuration if debug library is absent. set(FMOD_LIBRARY_DEBUG ${FMOD_LIBRARY_RELEASE}) endif (NOT FMOD_LIBRARY_DEBUG) else (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) - message(STATUS "No support for FMOD Ex audio (need to set FMODEX_SDK_DIR?)") + message(STATUS "No support for FMOD EX audio (need to set FMODEX_SDK_DIR?)") set(FMOD OFF) set(FMODEX OFF) endif (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) if (FMOD) message(STATUS "Building with FMOD Ex audio support") + set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODEX") endif (FMOD) diff --git a/indra/cmake/FMODSTUDIO.cmake b/indra/cmake/FMODSTUDIO.cmake index b2460cc79..a59bc0f43 100644 --- a/indra/cmake/FMODSTUDIO.cmake +++ b/indra/cmake/FMODSTUDIO.cmake @@ -3,45 +3,32 @@ include(Linking) if (FMODEX AND FMODSTUDIO) - message( FATAL_ERROR "You can not enable two FMOD variants at the same time." ) + message( FATAL_ERROR "You can not enable two FMOD variants at the same time." ) endif (FMODEX AND FMODSTUDIO) unset(FMOD_LIBRARY_RELEASE CACHE) unset(FMOD_LIBRARY_DEBUG CACHE) unset(FMOD_INCLUDE_DIR CACHE) -if(STANDALONE) - if (NOT FMODSTUDIO_SDK_DIR AND WINDOWS) - GET_FILENAME_COMPONENT(REG_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Studio\ API\ Windows] ABSOLUTE) - set(FMODSTUDIO_SDK_DIR ${REG_DIR} CACHE PATH "Path to the FMOD Studio SDK." FORCE) - endif (NOT FMODSTUDIO_SDK_DIR AND WINDOWS) - if(NOT FMODSTUDIO_SDK_DIR) - message(FATAL_ERROR "FMODSTUDIO_SDK_DIR not set!") - endif(NOT FMODSTUDIO_SDK_DIR) -endif(STANDALONE) +set(FMOD_EXTERNAL_LIB OFF) + +if(STANDALONE OR WINDOWS) + if (NOT FMODSTUDIO_SDK_DIR AND WINDOWS) + GET_FILENAME_COMPONENT(REG_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Studio\ API\ Windows] ABSOLUTE) + set(FMODSTUDIO_SDK_DIR ${REG_DIR} CACHE PATH "Path to the FMOD Studio SDK." FORCE) + endif (NOT FMODSTUDIO_SDK_DIR AND WINDOWS) + if(NOT FMODSTUDIO_SDK_DIR) + message(FATAL_ERROR "FMODSTUDIO_SDK_DIR not set!") + endif(NOT FMODSTUDIO_SDK_DIR) +endif(STANDALONE OR WINDOWS) if(FMODSTUDIO_SDK_DIR) if(LINUX AND WORD_SIZE EQUAL 32) - set(release_lib_paths ${release_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/x86/lib" ) - set(debug__lib_paths ${debug_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/x86/lib") + set(fmod_lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib/x86" ) elseif(LINUX) - set(release__lib_paths ${release_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/x86_64/lib") - set(debug_fmod_lib_paths ${debug_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/x86_64/lib") + set(fmod_lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib/x86_64") else(LINUX AND WORD_SIZE EQUAL 32) - set(release_fmod_lib_paths ${release_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib") - set(debug_fmod_lib_paths ${debug_fmod_lib_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib") - endif(LINUX AND WORD_SIZE EQUAL 32) - set(fmod_inc_paths ${fmod_inc_paths} "${FMODSTUDIO_SDK_DIR}/api/lowlevel/inc") - - if(LINUX AND WORD_SIZE EQUAL 32) - set(release_lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib/x86" ) - set(debug__lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lig/x86") - elseif(LINUX) - set(release__lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib/x86_64") - set(debug_fmod_lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib/x86_64") - else(LINUX AND WORD_SIZE EQUAL 32) - set(release_fmod_lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib") - set(debug_fmod_lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib") + set(fmod_lib_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib") endif(LINUX AND WORD_SIZE EQUAL 32) set(fmod_inc_paths "${FMODSTUDIO_SDK_DIR}/api/lowlevel/inc") @@ -50,16 +37,16 @@ if(FMODSTUDIO_SDK_DIR) set(CMAKE_FIND_LIBRARY_SUFFIXES .dll) endif(WINDOWS) if(WORD_SIZE EQUAL 64 AND WINDOWS) - find_library(FMOD_LIBRARY_RELEASE fmod64 PATHS ${release_fmod_lib_paths}) - find_library(FMOD_LIBRARY_DEBUG fmodL64 PATHS ${debug_fmod_lib_paths}) + find_library(FMOD_LIBRARY_RELEASE fmod64 PATHS ${fmod_lib_paths} NO_DEFAULT_PATH) + find_library(FMOD_LIBRARY_DEBUG fmodL64 PATHS ${fmod_lib_paths} NO_DEFAULT_PATH) else(WORD_SIZE EQUAL 64 AND WINDOWS)#Check if CMAKE_FIND_LIBRARY_PREFIXES is set to 'lib' for darwin. - find_library(FMOD_LIBRARY_RELEASE fmod PATHS ${release_fmod_lib_paths}) - find_library(FMOD_LIBRARY_DEBUG fmodL PATHS ${debug_fmod_lib_paths}) + find_library(FMOD_LIBRARY_RELEASE fmod PATHS ${fmod_lib_paths} NO_DEFAULT_PATH) + find_library(FMOD_LIBRARY_DEBUG fmodL PATHS ${fmod_lib_paths} NO_DEFAULT_PATH) endif(WORD_SIZE EQUAL 64 AND WINDOWS) if(WINDOWS) - set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_OLD}) - string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) - string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_OLD}) + string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) + string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) else(WINDOWS) set(FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) set(FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) @@ -68,11 +55,12 @@ if(FMODSTUDIO_SDK_DIR) if(NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) if(STANDALONE) message(FATAL_ERROR "Provided FMODSTUDIO_SDK_DIR path not found '{$FMODSTUDIO_SDK_DIR}'") - else(STANDALONE) - message(STATUS "Provided FMODSTUDIO_SDK_DIR path not found '${FMODSTUDIO_SDK_DIR}'. Falling back to prebuilts") - endif(STANDALONE) + else(STANDALONE) + message(STATUS "Provided FMODSTUDIO_SDK_DIR path not found '${FMODSTUDIO_SDK_DIR}'. Falling back to prebuilts") + endif(STANDALONE) else(NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) message(STATUS "Using system-provided FMOD Studio Libraries") + set(FMOD_EXTERNAL_LIB ON) endif(NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) endif (FMODSTUDIO_SDK_DIR) @@ -88,10 +76,10 @@ if (NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) if(WORD_SIZE EQUAL 64) set(FMOD_LIBRARY_RELEASE ${LIBS_PREBUILT_DIR}/lib/release/fmod64${lib_suffix}) set(FMOD_LIBRARY_DEBUG ${LIBS_PREBUILT_DIR}/lib/debug/fmodL64${lib_suffix}) - else(WORD_SIZE EQUAL 64) - set(FMOD_LIBRARY_RELEASE ${LIBS_PREBUILT_DIR}/lib/release/fmod${lib_suffix}) + else(WORD_SIZE EQUAL 64) + set(FMOD_LIBRARY_RELEASE ${LIBS_PREBUILT_DIR}/lib/release/fmod${lib_suffix}) set(FMOD_LIBRARY_DEBUG ${LIBS_PREBUILT_DIR}/lib/debug/fmodL${lib_suffix}) - endif(WORD_SIZE EQUAL 64) + endif(WORD_SIZE EQUAL 64) else(WINDOWS) set(FMOD_LIBRARY_RELEASE ${LIBS_PREBUILT_DIR}/lib/release/libfmod${lib_suffix}) set(FMOD_LIBRARY_DEBUG ${LIBS_PREBUILT_DIR}/lib/debug/libfmodL${lib_suffix}) @@ -99,8 +87,8 @@ if (NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) set(FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) set(FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) if(WINDOWS) - string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) - string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) + string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_RELEASE ${FMOD_LIBRARY_RELEASE}) + string(REPLACE ".dll" "_vc.lib" FMOD_LINK_LIBRARY_DEBUG ${FMOD_LIBRARY_DEBUG}) endif(WINDOWS) use_prebuilt_binary(fmodstudio) set(FMOD_INCLUDE_DIR @@ -120,4 +108,5 @@ endif (FMOD_LIBRARY_RELEASE AND FMOD_INCLUDE_DIR) if (FMOD) message(STATUS "Building with FMOD Studio audio support") + set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODSTUDIO") endif (FMOD) diff --git a/indra/cmake/OPENAL.cmake b/indra/cmake/OPENAL.cmake index c53d2e710..39ab8210d 100644 --- a/indra/cmake/OPENAL.cmake +++ b/indra/cmake/OPENAL.cmake @@ -2,6 +2,7 @@ include(Linking) include(Prebuilt) +if(NOT FMOD) if (LINUX) set(OPENAL ON CACHE BOOL "Enable OpenAL") else (LINUX) @@ -29,4 +30,6 @@ endif (OPENAL) if (OPENAL) message(STATUS "Building with OpenAL audio support") + set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_OPENAL") endif (OPENAL) +endif(NOT FMOD) diff --git a/indra/develop.py b/indra/develop.py index a5d1dd9d0..add325c41 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -815,10 +815,12 @@ Commands: Command-options for "configure": We use cmake variables to change the build configuration. -DPACKAGE:BOOL=ON Create "package" target to make installers - -DLOCALIZESETUP:BOOL=ON Create one win_setup target per supported language -DLL_TESTS:BOOL=OFF Don't generate unit test projects -DEXAMPLEPLUGIN:BOOL=OFF Don't generate example plugin project -DDISABLE_TCMALLOC:BOOL=ON Disable linkage of TCMalloc. (64bit builds automatically disable TCMalloc) + -DRELEASE_CRASH_REPORTING:BOOL=ON Enable Google Breakpad crash reporting + -DFMODSTUDIO:BOOL=ON Use FMOD Studio audio libraries + -DFMODEX:BOOL=ON Use FMOD Ex audio libraries Examples: Set up a Visual Studio 2010 project with "package" target: diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index dcf99a751..3091e430a 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1402,16 +1402,6 @@ if (WINDOWS) list(APPEND viewer_SOURCE_FILES ${viewer_INSTALLER_FILES}) endif (WINDOWS) -if (FMODSTUDIO) - set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODSTUDIO") -endif (FMODSTUDIO) -if (FMODEX) - set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODEX") -endif (FMODEX) -if (OPENAL) - set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_OPENAL") -endif (OPENAL) - set_source_files_properties(llstartup.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}") list(APPEND viewer_SOURCE_FILES ${viewer_HEADER_FILES}) @@ -1442,9 +1432,9 @@ if(FMOD_LIBRARY_RELEASE) add_custom_target(fmod_lib_copy DEPENDS fmod_lib.marker prepare) if(DARWIN) add_custom_command(OUTPUT fmod_lib_install.marker - COMMAND install_name_tool -id "@executable_path/../Resources/$<$>:${fmod_lib_rel_name}>$<$:${FMOD_LIBRARY_DEBUG}>" "${CMAKE_CFG_INTDIR}/$<$>:${fmod_lib_rel_name}>$<$:${fmod_lib_deb_name}>" + COMMAND install_name_tool -id "@executable_path/../Resources/$<$>:${fmod_lib_rel_name}>$<$:${FMOD_LIBRARY_DEBUG}>" "${CMAKE_CFG_INTDIR}/$<$>:${fmod_lib_rel_name}>$<$:${fmod_lib_deb_name}>" DEPENDS fmod_lib.marker) - add_dependencies(fmod_lib_copy fmod_lib_install.marker) + add_dependencies(fmod_lib_copy fmod_lib_install.marker) endif(DARWIN) add_dependencies(${VIEWER_BINARY_NAME} fmod_lib_copy) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index e7b3268fa..4079eb7bf 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -210,7 +210,7 @@ class ViewerManifest(LLManifest): except: config, file = (None, None) if(config == 'optimized'): - if(self.args['configuration'].lower() != 'release' and self.args['configuration'].lower() != 'relwithdebinfo'): + if(self.args['configuration'].lower() != 'release' and self.args['configuration'].lower() != 'relwithdebinfo' and self.args['configuration'].lower() != 'universal'): continue cur_path = file if(config == 'debug'): @@ -218,7 +218,10 @@ class ViewerManifest(LLManifest): continue cur_path = file if(cur_path != ''): - found_libs += self.path_optional(cur_path) + if sys.platform == "linux" or sys.platform == "linux2": + found_libs += self.path_optional(cur_path+"*") + else: + found_libs += self.path_optional(cur_path) return found_libs class WindowsManifest(ViewerManifest): @@ -578,7 +581,7 @@ class DarwinManifest(ViewerManifest): self.end_prefix() - if self.prefix(src=self.args['configuration'], alt_build=libdir): + if self.prefix(src= '' if self.args['configuration'].lower() == 'universal' else self.args['configuration'], alt_build=libdir): dylibs += self.add_extra_libraries() self.end_prefix() @@ -880,7 +883,7 @@ class Linux_i686Manifest(LinuxManifest): self.end_prefix("lib") - if (not self.standalone()) and self.prefix(src=self.args['configuration'], alt_build="../packages/lib/release", dst="lib"): + if (not self.standalone()) and self.prefix(src='', alt_build="../packages/lib/release", dst="lib"): self.add_extra_libraries() self.end_prefix() @@ -932,7 +935,7 @@ class Linux_x86_64Manifest(LinuxManifest): self.end_prefix("lib") - if (not self.standalone()) and self.prefix(src=self.args['configuration'], alt_build="../packages/lib/release", dst="lib"): + if (not self.standalone()) and self.prefix(src='', alt_build="../packages/lib/release", dst="lib"): self.add_extra_libraries() self.end_prefix() From 56bd48bd2a3d288fa446791caaf738f49f555353 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Fri, 23 Jan 2015 02:07:19 -0600 Subject: [PATCH 54/55] elseif (WINDOWS AND WORD_SIZE EQUAL 32)' mistakenly clobbered. Also, try 'ProgramFiles(x86)' envvar first --- indra/cmake/QuickTimePlugin.cmake | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/indra/cmake/QuickTimePlugin.cmake b/indra/cmake/QuickTimePlugin.cmake index b769b29ba..171213d42 100644 --- a/indra/cmake/QuickTimePlugin.cmake +++ b/indra/cmake/QuickTimePlugin.cmake @@ -8,8 +8,12 @@ endif(INSTALL_PROPRIETARY) if (DARWIN) include(CMakeFindFrameworks) find_library(QUICKTIME_LIBRARY QuickTime) - - SET(program_files $ENV{ProgramW6432}) +elseif (WINDOWS AND WORD_SIZE EQUAL 32) + SET(program_files "ProgramFiles(x86)") + SET(program_files $ENV{${program_files}}) + if(NOT program_files) + SET(program_files $ENV{ProgramW6432}) + endif(NOT program_files) if(NOT program_files) SET(program_files $ENV{ProgramFiles}) endif(NOT program_files) From 6e4ef6f8a1e6a843e749fc438d44d13d54a0ba5a Mon Sep 17 00:00:00 2001 From: Shyotl Date: Fri, 23 Jan 2015 16:49:21 -0600 Subject: [PATCH 55/55] I'll have all the derps, please. --- indra/cmake/FMODEX.cmake | 2 +- indra/cmake/FMODSTUDIO.cmake | 2 +- indra/newview/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/cmake/FMODEX.cmake b/indra/cmake/FMODEX.cmake index eb8712207..ae95866c4 100644 --- a/indra/cmake/FMODEX.cmake +++ b/indra/cmake/FMODEX.cmake @@ -67,7 +67,7 @@ if (NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) if(WINDOWS) set(lib_suffix .dll) elseif(DARWIN) - set(lib_suffix .dynlib) + set(lib_suffix .dylib) else(WINDOWS) set(lib_suffix .so) endif(WINDOWS) diff --git a/indra/cmake/FMODSTUDIO.cmake b/indra/cmake/FMODSTUDIO.cmake index a59bc0f43..0939540f4 100644 --- a/indra/cmake/FMODSTUDIO.cmake +++ b/indra/cmake/FMODSTUDIO.cmake @@ -68,7 +68,7 @@ if (NOT FMOD_LIBRARY_RELEASE OR NOT FMOD_INCLUDE_DIR) if(WINDOWS) set(lib_suffix .dll) elseif(DARWIN) - set(lib_suffix .dynlib) + set(lib_suffix .dylib) else(WINDOWS) set(lib_suffix .so) endif(WINDOWS) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 3091e430a..23101bb56 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1438,7 +1438,7 @@ if(FMOD_LIBRARY_RELEASE) endif(DARWIN) add_dependencies(${VIEWER_BINARY_NAME} fmod_lib_copy) - #viewer_manifest.py needs these libraries (for now its all dynlib) + #viewer_manifest.py needs these libraries (for now its all dylib) if(MANIFEST_LIBRARIES) set(MANIFEST_LIBRARIES ${MANIFEST_LIBRARIES}|optimized ${fmod_lib_rel_name}|debug ${fmod_lib_deb_name}) else(MANIFEST_LIBRARIES)