Merge branch 'release/2025.03' of https://github.com/secondlife/viewer

# Conflicts:
#	indra/llaudio/llaudioengine_openal.cpp
#	indra/llui/lltextbox.h
#	indra/newview/llnotificationhandlerutil.cpp
#	indra/newview/llviewertexture.cpp
#	indra/newview/skins/default/xui/de/strings.xml
#	indra/newview/skins/default/xui/es/strings.xml
#	indra/newview/skins/default/xui/fr/strings.xml
#	indra/newview/skins/default/xui/it/strings.xml
#	indra/newview/skins/default/xui/pl/strings.xml
#	indra/newview/skins/default/xui/pt/strings.xml
#	indra/newview/skins/default/xui/ru/strings.xml
#	indra/newview/skins/default/xui/tr/strings.xml
master
Ansariel 2025-03-08 13:15:21 +01:00
commit d49359c086
33 changed files with 130 additions and 81 deletions

View File

@ -2326,11 +2326,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>archive</key>
<map>
<key>hash</key>
<string>191e4ef07a35f7147708415465191ce7622e3012</string>
<string>a3cc405d48a48a474d05b3de3d28da2005d80037</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
<string>https://github.com/secondlife/3p-openal-soft/releases/download/v1.23.1-8668009/openal-1.23.1-darwin64-8979520327.tar.zst</string>
<string>https://github.com/secondlife/3p-openal-soft/releases/download/v1.24.2-r1/openal-1.24.2-r1-darwin64-13245988487.tar.zst</string>
</map>
<key>name</key>
<string>darwin64</string>
@ -2340,11 +2340,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>archive</key>
<map>
<key>hash</key>
<string>3bd8c9028ef42bdb43c7422e7d324e213fdb081e</string>
<string>a2b63f0f85ca156c59ee1d34ef96c8e50b89153c</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
<string>https://github.com/secondlife/3p-openal-soft/releases/download/v1.23.1-8668009/openal-1.23.1-linux64-8979520327.tar.zst</string>
<string>https://github.com/secondlife/3p-openal-soft/releases/download/v1.24.2-r1/openal-1.24.2-r1-linux64-13245988487.tar.zst</string>
</map>
<key>name</key>
<string>linux64</string>
@ -2354,11 +2354,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>archive</key>
<map>
<key>hash</key>
<string>4b849609abec790e89be5fad8ddee3717ee301c4</string>
<string>8ad24fba1191c9cb0d2ab36e64b04b4648a99f43</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
<string>https://github.com/secondlife/3p-openal-soft/releases/download/v1.23.1-8668009/openal-1.23.1-windows64-8979520327.tar.zst</string>
<string>https://github.com/secondlife/3p-openal-soft/releases/download/v1.24.2-r1/openal-1.24.2-r1-windows64-13245988487.tar.zst</string>
</map>
<key>name</key>
<string>windows64</string>

View File

@ -190,7 +190,7 @@ LLAudioChannelOpenAL::~LLAudioChannelOpenAL()
void LLAudioChannelOpenAL::cleanup()
{
alSourceStop(mALSource);
alSourcei(mALSource, AL_BUFFER, 0); // <ND/> need to unset buffer too, or alDeleteBuffers will fail.
alSourcei(mALSource, AL_BUFFER, AL_NONE);
mCurrentBufferp = NULL;
}

View File

@ -304,6 +304,8 @@ S32 LLPacketRing::drainSocket(S32 socket)
S32 num_dropped_packets = (num_loops - 1 + old_num_packets) - mNumBufferedPackets;
if (num_dropped_packets > 0)
{
// It will eventually be accounted by mDroppedPackets
// and mPacketsLost, but track it here for logging purposes.
mNumDroppedPackets += num_dropped_packets;
}
return (S32)(mNumBufferedPackets);

View File

@ -406,9 +406,14 @@ void LLPluginProcessParent::idle(void)
apr_sockaddr_t* addr = NULL;
mListenSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
mBoundPort = 0;
if (!mListenSocket)
{
killSockets();
errorState();
break;
}
// This code is based on parts of LLSocket::create() in lliosocket.cpp.
status = apr_sockaddr_info_get(
&addr,
"127.0.0.1",

View File

@ -3861,7 +3861,7 @@ bool LLScrollListCtrl::highlightMatchingItems(const std::string& filter_str)
bool res = false;
setHighlightedColor(LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red));
setHighlightedColor(LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red4));
std::string filter_str_lc(filter_str);
LLStringUtil::toLower(filter_str_lc);

View File

@ -45,7 +45,7 @@ namespace ll
const LLColor4& getHighlightColor( ) const
{
static LLUIColor highlight_color = LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red);
static LLUIColor highlight_color = LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red4);
return highlight_color.get();
}

View File

@ -81,13 +81,12 @@ protected:
LLUIString mText;
callback_t mClickedCallback;
bool mShowCursorHand;
// <FS:ND> Searchable text for UI filter
protected:
virtual std::string _getSearchText() const
{
return LLTextBase::_getSearchText() + mText.getString();
}
// </FS:ND>
};
// Build time optimization, generate once in .cpp file

View File

@ -11232,6 +11232,17 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>768</integer>
</map>
<key>RenderTextureVRAMDivisor</key>
<map>
<key>Comment</key>
<string>Divisor for maximum amount of VRAM the viewer will use for textures. 1 = all the VRAM. Used in conjunction with RenderMaxVRAMBudget.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>2</integer>
</map>
<key>RenderMinFreeMainMemoryThreshold</key>
<map>
<key>Comment</key>
@ -15428,7 +15439,7 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Type</key>
<string>F32</string>
<key>Value</key>
<real>0.04</real>
<real>0.0095</real>
</map>
<key>TextureScaleMaxAreaFactor</key>
<map>

View File

@ -80,6 +80,17 @@ vec3 atmosFragLightingLinear(vec3 light, vec3 additive, vec3 atten);
vec4 decodeNormal(vec4 norm);
vec3 clampHDRRange(vec3 color)
{
// Why do this?
// There are situations where the color range will go to something insane - potentially producing infs and NaNs even.
// This is a safety measure to prevent that.
// As to the specific number there - allegedly some HDR displays expect values to be in the 0-11.2 range. Citation needed.
// -Geenz 2025-03-05
color = mix(color, vec3(0.0), isnan(color));
return clamp(color, vec3(0.0), vec3(11.2));
}
float calcLegacyDistanceAttenuation(float distance, float falloff)
{
float dist_atten = 1.0 - clamp((distance + falloff)/(1.0 + falloff), 0.0, 1.0);

View File

@ -69,6 +69,8 @@ void dofSampleNear(inout vec4 diff, inout float w, float min_sc, vec2 tc)
w += wg;
}
vec3 clampHDRRange(vec3 color);
void main()
{
vec2 tc = vary_fragcoord.xy;
@ -120,5 +122,6 @@ void main()
diff /= w;
}
diff.rgb = clampHDRRange(diff.rgb);
frag_color = diff;
}

View File

@ -43,6 +43,8 @@ vec3 legacyGamma(vec3 color)
return c;
}
vec3 clampHDRRange(vec3 color);
void main()
{
//this is the one of the rare spots where diffuseRect contains linear color values (not sRGB)
@ -53,6 +55,7 @@ void main()
diff.rgb = legacyGamma(diff.rgb);
#endif
frag_color = max(diff, vec4(0));
diff.rgb = clampHDRRange(diff.rgb);
frag_color = diff;
}

View File

@ -71,6 +71,7 @@ float noise(vec2 x) {
//=============================
vec3 clampHDRRange(vec3 color);
void main()
@ -84,6 +85,7 @@ void main()
diff.rgb += nz*0.003;
#endif
diff.rgb = clampHDRRange(diff.rgb);
frag_color = diff;
gl_FragDepth = texture(depthMap, vary_fragcoord.xy).r;

View File

@ -34,6 +34,8 @@ in vec2 vary_fragcoord;
vec3 linear_to_srgb(vec3 cl);
vec3 toneMap(vec3 color);
vec3 clampHDRRange(vec3 color);
void main()
{
//this is the one of the rare spots where diffuseRect contains linear color values (not sRGB)
@ -45,6 +47,7 @@ void main()
diff.rgb = clamp(diff.rgb, vec3(0.0), vec3(1.0));
#endif
diff.rgb = clampHDRRange(diff.rgb);
//debugExposure(diff.rgb);
frag_color = max(diff, vec4(0));
}

View File

@ -104,6 +104,7 @@ vec3 pbrBaseLight(vec3 diffuseColor,
vec3 atten);
GBufferInfo getGBuffer(vec2 screenpos);
vec3 clampHDRRange(vec3 color);
void adjustIrradiance(inout vec3 irradiance, float ambocc)
{
@ -278,6 +279,7 @@ void main()
float final_scale = 1;
if (classic_mode > 0)
final_scale = 1.1;
frag_color.rgb = max(color.rgb * final_scale, vec3(0)); //output linear since local lights will be added to this shader's results
frag_color.rgb = clampHDRRange(color.rgb * final_scale); //output linear since local lights will be added to this shader's results
frag_color.a = 0.0;
}

View File

@ -263,7 +263,12 @@ void main()
vec3 refPos = getPositionWithNDC(vec3(distort*2.0-vec2(1.0), depth*2.0-1.0));
// Calculate some distance fade in the water to better assist with refraction blending and reducing the refraction texture's "disconnect".
fade = max(0,min(1, (pos.z - refPos.z) / 10)) * water_mask;
#ifdef SHORELINE_FADE
fade = max(0,min(1, (pos.z - refPos.z) / 10))
#else
fade = 1 * water_mask;
#endif
distort2 = mix(distort, distort2, min(1, fade * 10));
depth = texture(depthMap, distort2).r;

View File

@ -4098,7 +4098,6 @@ LLSD LLAppViewer::getViewerInfo() const
info["PACKETS_LOST"] = LLViewerStats::instance().getRecording().getSum(LLStatViewer::PACKETS_LOST);
info["PACKETS_IN"] = packets_in;
info["PACKETS_PCT"] = 100.f*info["PACKETS_LOST"].asReal() / info["PACKETS_IN"].asReal();
info["PACKETS_DROPPED"] = LLViewerStats::instance().getRecording().getSum(LLStatViewer::PACKETS_DROPPED);
}
if (mServerReleaseNotesURL.empty())

View File

@ -33,6 +33,7 @@
// viewer includes
#include "llagent.h"
#include "llagentcamera.h"
#include "llcriticaldamp.h"
#include "llface.h"
#include "lllightconstants.h"
@ -785,6 +786,14 @@ bool LLDrawable::updateMove()
makeActive();
// #3256 force undampened movement for attached objects in mouselook
// to prevent animation bork for linkset with animated parts
if (!isRoot() && gAgentCamera.cameraMouselook() &&
!mVObjp->isRiggedMesh() && mVObjp->getAvatar() && mVObjp->getAvatar()->isSelf())
{
return updateMoveUndamped();
}
return isState(MOVE_UNDAMPED) ? updateMoveUndamped() : updateMoveDamped();
}

View File

@ -911,6 +911,13 @@ void LLPanelRegionInfo::initCtrl(const std::string& name)
getChild<LLUICtrl>(name)->setCommitCallback(boost::bind(&LLPanelRegionInfo::onChangeAnything, this));
}
void LLPanelRegionInfo::initAndSetTexCtrl(LLTextureCtrl*& ctrl, const std::string& name)
{
ctrl = findChild<LLTextureCtrl>(name);
if (ctrl)
ctrl->setOnSelectCallback([this](LLUICtrl* ctrl, const LLSD& param){ onChangeAnything(); });
}
template<typename CTRL>
void LLPanelRegionInfo::initAndSetCtrl(CTRL*& ctrl, const std::string& name)
{
@ -1766,7 +1773,7 @@ bool LLPanelRegionTerrainInfo::postBuild()
for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
{
initAndSetCtrl(mTextureDetailCtrl[i], llformat("texture_detail_%d", i));
initAndSetTexCtrl(mTextureDetailCtrl[i], llformat("texture_detail_%d", i));
if (mTextureDetailCtrl[i])
{
mTextureDetailCtrl[i]->setBakeTextureEnabled(false);

View File

@ -165,6 +165,7 @@ public:
protected:
void initCtrl(const std::string& name);
template<typename CTRL> void initAndSetCtrl(CTRL*& ctrl, const std::string& name);
void initAndSetTexCtrl(LLTextureCtrl*& ctrl, const std::string& name);
// Returns true if update sent and apply button should be
// disabled.

View File

@ -1121,14 +1121,6 @@ void LLIMProcessing::processNewMessage(LLUUID from_id,
asset_type = (LLAssetType::EType)(atoi((*(iter++)).c_str()));
iter++; // wearable type if applicable, otherwise asset type
item_name = std::string((*(iter++)).c_str());
// Note There is more elements in 'tokens' ...
for (int i = 0; i < 6; i++)
{
LL_WARNS() << *(iter++) << LL_ENDL;
iter++;
}
}
}
else

View File

@ -4064,6 +4064,7 @@ void LLIMMgr::inviteToSession(
&& voice_invite && "VoiceInviteQuestionDefault" == question_type)
{
LL_INFOS("IMVIEW") << "Rejecting voice call from initiating muted resident " << caller_name << LL_ENDL;
payload["voice_channel_info"] = voice_channel_info;
LLIncomingCallDialog::processCallResponse(1, payload);
return;
}
@ -4120,6 +4121,7 @@ void LLIMMgr::inviteToSession(
send_do_not_disturb_message(gMessageSystem, caller_id, session_id);
}
// silently decline the call
payload["voice_channel_info"] = voice_channel_info;
LLIncomingCallDialog::processCallResponse(1, payload);
return;
}

View File

@ -1006,6 +1006,11 @@ bool LLLogChat::isTranscriptFileFound(std::string fullname)
return result;
}
std::string LLLogChat::getGroupChatSuffix()
{
return GROUP_CHAT_SUFFIX;
}
//*TODO mark object's names in a special way so that they will be distinguishable form avatar name
//which are more strict by its nature (only firstname and secondname)
//Example, an object's name can be written like "Object <actual_object's_name>"

View File

@ -127,6 +127,8 @@ public:
static bool isAdHocTranscriptExist(std::string file_name);
static bool isTranscriptFileFound(std::string fullname);
static std::string getGroupChatSuffix();
bool historyThreadsFinished(LLUUID session_id);
LLLoadHistoryThread* getLoadHistoryThread(LLUUID session_id);
LLDeleteHistoryThread* getDeleteHistoryThread(LLUUID session_id);

View File

@ -108,24 +108,28 @@ void LLHandlerUtil::logToIM(const EInstantMessage& session_type,
from = SYSTEM_FROM;
}
// Build a new format username or firstname_lastname for legacy names
// to use it for a history log filename.
// <FS:Ansariel> [Legacy IM logfile names]
//std::string user_name = LLCacheName::buildUsername(session_name);
std::string user_name(session_name);
if (!gAgent.isInGroup(session_owner_id))
std::string file_name;
if (session_type == IM_SESSION_GROUP_START)
{
file_name = session_name + LLLogChat::getGroupChatSuffix();
}
else
{
// Build a new format username or firstname_lastname for legacy names
// to use it for a history log filename.
// <FS:Ansariel> [Legacy IM logfile names]
//file_name = LLCacheName::buildUsername(session_name);
if (gSavedSettings.getBOOL("UseLegacyIMLogNames"))
{
user_name = session_name.substr(0, session_name.find(" Resident"));;
file_name = session_name.substr(0, session_name.find(" Resident"));;
}
else
{
user_name = LLCacheName::buildUsername(session_name);
file_name = LLCacheName::buildUsername(session_name);
}
// </FS:Ansariel> [Legacy IM logfile names]
}
// </FS:Ansariel> [Legacy IM logfile names]
LLIMModel::instance().logToFile(user_name, from, from_id, message);
LLIMModel::instance().logToFile(file_name, from, from_id, message);
}
else
{

View File

@ -588,12 +588,13 @@ bool LLToolDragAndDrop::handleKey(KEY key, MASK mask)
bool LLToolDragAndDrop::handleToolTip(S32 x, S32 y, MASK mask)
{
const F32 DRAG_N_DROP_TOOLTIP_DELAY = 0.1f;
if (!mToolTipMsg.empty())
{
LLToolTipMgr::instance().unblockToolTips();
LLToolTipMgr::instance().show(LLToolTip::Params()
.message(mToolTipMsg)
.delay_time(gSavedSettings.getF32( "DragAndDropToolTipDelay" )));
.delay_time(DRAG_N_DROP_TOOLTIP_DELAY));
return true;
}
return false;

View File

@ -121,7 +121,6 @@ LLTrace::CountStatHandle<> FPS("FPS", "Frames rendered"),
PACKETS_IN("Packets In", "Packets received"),
PACKETS_LOST("packetsloststat", "Packets lost"),
PACKETS_OUT("packetsoutstat", "Packets sent"),
PACKETS_DROPPED("packetsdropped", "Packets dropped"),
TEXTURE_PACKETS("texturepacketsstat", "Texture data packets received"),
CHAT_COUNT("chatcount", "Chat messages sent"),
IM_COUNT("imcount", "IMs sent"),
@ -654,7 +653,6 @@ void send_viewer_stats(bool include_preferences)
fail["send_packet"] = (S32) gMessageSystem->mSendPacketFailureCount;
fail["dropped"] = (S32) gMessageSystem->mDroppedPackets;
fail["ring_dropped"] = (S32)gMessageSystem->mPacketRing.getNumDroppedPackets();
fail["resent"] = (S32) gMessageSystem->mResentPackets;
fail["failed_resends"] = (S32) gMessageSystem->mFailedResendPackets;
fail["off_circuit"] = (S32) gMessageSystem->mOffCircuitPackets;

View File

@ -119,7 +119,6 @@ extern LLTrace::CountStatHandle<> FPS,
PACKETS_IN,
PACKETS_LOST,
PACKETS_OUT,
PACKETS_DROPPED,
TEXTURE_PACKETS,
CHAT_COUNT,
IM_COUNT,

View File

@ -507,7 +507,12 @@ void LLViewerTexture::updateClass()
}
LLViewerMediaTexture::updateClass();
// This is a divisor used to determine how much VRAM from our overall VRAM budget to use.
// This is **cumulative** on whatever the detected or manually set VRAM budget is.
// If we detect 2048MB of VRAM, this will, by default, only use 1024.
// If you set 1024MB of VRAM, this will, by default, use 512.
// -Geenz 2025-03-03
static LLCachedControl<U32> tex_vram_divisor(gSavedSettings, "RenderTextureVRAMDivisor", 2);
static LLCachedControl<U32> max_vram_budget(gSavedSettings, "RenderMaxVRAMBudget", 0);
static LLCachedControl<bool> max_vram_budget_enabled(gSavedSettings, "FSLimitTextureVRAMUsage"); // <FS:Ansariel> Expose max texture VRAM setting
@ -521,6 +526,7 @@ void LLViewerTexture::updateClass()
// <FS:Ansariel> Expose max texture VRAM setting
//F32 budget = max_vram_budget == 0 ? (F32)gGLManager.mVRAM : (F32)max_vram_budget;
F32 budget = !max_vram_budget_enabled ? (F32)gGLManager.mVRAM : (F32)max_vram_budget;
budget /= tex_vram_divisor;
// Try to leave at least half a GB for everyone else and for bias,
// but keep at least 768MB for ourselves

View File

@ -914,7 +914,7 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
if (imagep->getBoostLevel() < LLViewerFetchedTexture::BOOST_HIGH) // don't bother checking face list for boosted textures
{
static LLCachedControl<F32> texture_scale_min(gSavedSettings, "TextureScaleMinAreaFactor", 0.04f);
static LLCachedControl<F32> texture_scale_min(gSavedSettings, "TextureScaleMinAreaFactor", 0.0095f);
static LLCachedControl<F32> texture_scale_max(gSavedSettings, "TextureScaleMaxAreaFactor", 25.f);
F32 max_vsize = 0.f;
@ -923,7 +923,8 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
U32 face_count = 0;
// get adjusted bias based on image resolution
F32 max_discard = F32(imagep->getMaxDiscardLevel());
LLImageGL* img = imagep->getGLTexture();
F32 max_discard = F32(img ? img->getMaxDiscardLevel() : MAX_DISCARD_LEVEL);
F32 bias = llclamp(max_discard - 2.f, 1.f, LLViewerTexture::sDesiredDiscardBias);
// convert bias into a vsize scaler
@ -1000,8 +1001,9 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
// Scale desired texture resolution higher or lower depending on texture scale
//
// Minimum usage examples: a 1024x1024 texture with aplhabet, runing string
// shows one letter at a time
// Minimum usage examples: a 1024x1024 texture with aplhabet (texture atlas),
// runing string shows one letter at a time. If texture has ten 100px symbols
// per side, minimal scale is (100/1024)^2 = 0.0095
//
// Maximum usage examples: huge chunk of terrain repeats texture
// TODO: make this work with the GLTF texture transforms

View File

@ -304,7 +304,6 @@ void LLViewerThrottle::updateDynamicThrottle()
}
mUpdateTimer.reset();
// Todo: account for dropped packets from LLPacketRing (or make the thing threaded)
LLUnit<F32, LLUnits::Percent> mean_packets_lost = LLViewerStats::instance().getRecording().getMean(LLStatViewer::PACKETS_LOST_PERCENT);
if (mean_packets_lost > TIGHTEN_THROTTLE_THRESHOLD)
{

View File

@ -1234,7 +1234,6 @@ void LLWorld::updateNetStats()
S32 packets_in = gMessageSystem->mPacketsIn - mLastPacketsIn;
S32 packets_out = gMessageSystem->mPacketsOut - mLastPacketsOut;
S32 packets_lost = gMessageSystem->mDroppedPackets - mLastPacketsLost;
S32 ring_packets_dropped = gMessageSystem->mPacketRing.getNumDroppedPackets();
F64Bits actual_in_bits(gMessageSystem->mPacketRing.getAndResetActualInBits());
F64Bits actual_out_bits(gMessageSystem->mPacketRing.getAndResetActualOutBits());
@ -1245,7 +1244,6 @@ void LLWorld::updateNetStats()
add(LLStatViewer::PACKETS_IN, packets_in);
add(LLStatViewer::PACKETS_OUT, packets_out);
add(LLStatViewer::PACKETS_LOST, packets_lost);
add(LLStatViewer::PACKETS_DROPPED, ring_packets_dropped);
F32 total_packets_in = (F32)LLViewerStats::instance().getRecording().getSum(LLStatViewer::PACKETS_IN);
if (total_packets_in > 0.f)

View File

@ -427,6 +427,16 @@
layout="topleft"
top="80"
left="213"
name="0_lbl"
width="7">
0
</text>
<text
follows="left|top"
font="SansSerifSmall"
height="18"
layout="topleft"
left_pad="31"
name="1_lbl"
width="7">
1
@ -436,7 +446,7 @@
font="SansSerifSmall"
height="18"
layout="topleft"
left_pad="31"
left_pad="30"
name="2_lbl"
width="7">
2
@ -479,16 +489,6 @@
left_pad="30"
name="6_lbl"
width="7">
6
</text>
<text
follows="left|top"
font="SansSerifSmall"
height="18"
layout="topleft"
left_pad="30"
name="7_lbl"
width="7">
7
6
</text>
</panel>

View File

@ -96,25 +96,4 @@
<text name="photo_desc">
0.04.0の値を入れてください)
</text>
<text name="1_lbl">
1
</text>
<text name="2_lbl">
2
</text>
<text name="3_lbl">
3
</text>
<text name="4_lbl">
4
</text>
<text name="5_lbl">
5
</text>
<text name="6_lbl">
6
</text>
<text name="7_lbl">
7
</text>
</panel>